Add metadata support
- Begin WIP on Bit Badger theme
|
@ -342,8 +342,10 @@ module Page =
|
|||
"permalink", page.permalink
|
||||
"updatedOn", page.updatedOn
|
||||
"showInPageList", page.showInPageList
|
||||
"template", page.template
|
||||
"text", page.text
|
||||
"priorPermalinks", page.priorPermalinks
|
||||
"metadata", page.metadata
|
||||
"revisions", page.revisions
|
||||
]
|
||||
write; withRetryDefault; ignoreResult
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
namespace MyWebLog
|
||||
|
||||
open System
|
||||
open MyWebLog
|
||||
|
||||
/// A category under which a post may be identified
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
|
@ -119,6 +120,9 @@ type Page =
|
|||
/// The current text of the page
|
||||
text : string
|
||||
|
||||
/// Metadata for this page
|
||||
metadata : MetaItem list
|
||||
|
||||
/// Permalinks at which this page may have been previously served (useful for migrated content)
|
||||
priorPermalinks : Permalink list
|
||||
|
||||
|
@ -141,6 +145,7 @@ module Page =
|
|||
showInPageList = false
|
||||
template = None
|
||||
text = ""
|
||||
metadata = []
|
||||
priorPermalinks = []
|
||||
revisions = []
|
||||
}
|
||||
|
@ -182,6 +187,9 @@ type Post =
|
|||
/// The tags for the post
|
||||
tags : string list
|
||||
|
||||
/// Metadata for the post
|
||||
metadata : MetaItem list
|
||||
|
||||
/// Permalinks at which this post may have been previously served (useful for migrated content)
|
||||
priorPermalinks : Permalink list
|
||||
|
||||
|
@ -205,6 +213,7 @@ module Post =
|
|||
text = ""
|
||||
categoryIds = []
|
||||
tags = []
|
||||
metadata = []
|
||||
priorPermalinks = []
|
||||
revisions = []
|
||||
}
|
||||
|
|
|
@ -98,6 +98,13 @@ type MetaItem =
|
|||
value : string
|
||||
}
|
||||
|
||||
/// Functions to support metadata items
|
||||
module MetaItem =
|
||||
|
||||
/// An empty metadata item
|
||||
let empty =
|
||||
{ name = ""; value = "" }
|
||||
|
||||
|
||||
/// A revision of a page or post
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
namespace MyWebLog.ViewModels
|
||||
|
||||
open System
|
||||
open System.Collections.Generic
|
||||
open MyWebLog
|
||||
|
||||
/// Details about a category, used to display category lists
|
||||
|
@ -136,13 +135,21 @@ type EditPageModel =
|
|||
|
||||
/// The text of the page
|
||||
text : string
|
||||
|
||||
/// Names of metadata items
|
||||
metaNames : string[]
|
||||
|
||||
/// Values of metadata items
|
||||
metaValues : string[]
|
||||
}
|
||||
|
||||
/// Create an edit model from an existing page
|
||||
static member fromPage (page : Page) =
|
||||
let latest =
|
||||
match page.revisions |> List.sortByDescending (fun r -> r.asOf) |> List.tryHead with
|
||||
| Some rev -> rev
|
||||
| None -> Revision.empty
|
||||
let page = if page.metadata |> List.isEmpty then { page with metadata = [ MetaItem.empty ] } else page
|
||||
{ pageId = PageId.toString page.id
|
||||
title = page.title
|
||||
permalink = Permalink.toString page.permalink
|
||||
|
@ -150,6 +157,8 @@ type EditPageModel =
|
|||
isShownInPageList = page.showInPageList
|
||||
source = MarkupText.sourceType latest.text
|
||||
text = MarkupText.text latest.text
|
||||
metaNames = page.metadata |> List.map (fun m -> m.name) |> Array.ofList
|
||||
metaValues = page.metadata |> List.map (fun m -> m.value) |> Array.ofList
|
||||
}
|
||||
|
||||
|
||||
|
@ -182,6 +191,12 @@ type EditPostModel =
|
|||
|
||||
/// Whether this post should be published
|
||||
doPublish : bool
|
||||
|
||||
/// Names of metadata items
|
||||
metaNames : string[]
|
||||
|
||||
/// Values of metadata items
|
||||
metaValues : string[]
|
||||
}
|
||||
/// Create an edit model from an existing past
|
||||
static member fromPost (post : Post) =
|
||||
|
@ -189,15 +204,18 @@ type EditPostModel =
|
|||
match post.revisions |> List.sortByDescending (fun r -> r.asOf) |> List.tryHead with
|
||||
| Some rev -> rev
|
||||
| None -> Revision.empty
|
||||
{ postId = PostId.toString post.id
|
||||
title = post.title
|
||||
permalink = Permalink.toString post.permalink
|
||||
source = MarkupText.sourceType latest.text
|
||||
text = MarkupText.text latest.text
|
||||
tags = String.Join (", ", post.tags)
|
||||
categoryIds = post.categoryIds |> List.map CategoryId.toString |> Array.ofList
|
||||
status = PostStatus.toString post.status
|
||||
doPublish = false
|
||||
let post = if post.metadata |> List.isEmpty then { post with metadata = [ MetaItem.empty ] } else post
|
||||
{ postId = PostId.toString post.id
|
||||
title = post.title
|
||||
permalink = Permalink.toString post.permalink
|
||||
source = MarkupText.sourceType latest.text
|
||||
text = MarkupText.text latest.text
|
||||
tags = String.Join (", ", post.tags)
|
||||
categoryIds = post.categoryIds |> List.map CategoryId.toString |> Array.ofList
|
||||
status = PostStatus.toString post.status
|
||||
doPublish = false
|
||||
metaNames = post.metadata |> List.map (fun m -> m.name) |> Array.ofList
|
||||
metaValues = post.metadata |> List.map (fun m -> m.value) |> Array.ofList
|
||||
}
|
||||
|
||||
|
||||
|
@ -251,6 +269,9 @@ type PostListItem =
|
|||
|
||||
/// Tags for the post
|
||||
tags : string list
|
||||
|
||||
/// Metadata for the post
|
||||
meta : MetaItem list
|
||||
}
|
||||
|
||||
/// Create a post list item from a post
|
||||
|
@ -265,6 +286,7 @@ type PostListItem =
|
|||
text = post.text
|
||||
categoryIds = post.categoryIds |> List.map CategoryId.toString
|
||||
tags = post.tags
|
||||
meta = post.metadata
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -361,10 +361,13 @@ module Page =
|
|||
}
|
||||
match result with
|
||||
| Some (title, page) ->
|
||||
let model = EditPageModel.fromPage page
|
||||
return!
|
||||
Hash.FromAnonymousObject {|
|
||||
csrf = csrfToken ctx
|
||||
model = EditPageModel.fromPage page
|
||||
model = model
|
||||
metadata = Array.zip model.metaNames model.metaValues
|
||||
|> Array.mapi (fun idx (name, value) -> [| string idx; name; value |])
|
||||
page_title = title
|
||||
templates = templatesForTheme ctx "page"
|
||||
|}
|
||||
|
@ -408,6 +411,10 @@ module Page =
|
|||
showInPageList = model.isShownInPageList
|
||||
template = match model.template with "" -> None | tmpl -> Some tmpl
|
||||
text = MarkupText.toHtml revision.text
|
||||
metadata = Seq.zip model.metaNames model.metaValues
|
||||
|> Seq.filter (fun it -> fst it > "")
|
||||
|> Seq.map (fun it -> { name = fst it; value = snd it })
|
||||
|> List.ofSeq
|
||||
revisions = revision :: page.revisions
|
||||
}
|
||||
do! (match model.pageId with "new" -> Data.Page.add | _ -> Data.Page.update) page conn
|
||||
|
|
|
@ -25,8 +25,8 @@ type WebLogMiddleware (next : RequestDelegate) =
|
|||
/// DotLiquid filters
|
||||
module DotLiquidBespoke =
|
||||
|
||||
open DotLiquid
|
||||
open System.IO
|
||||
open DotLiquid
|
||||
|
||||
/// A filter to generate nav links, highlighting the active link (exact match)
|
||||
type NavLinkFilter () =
|
||||
|
@ -166,7 +166,7 @@ let main args =
|
|||
builder.Services
|
||||
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie(fun opts ->
|
||||
opts.ExpireTimeSpan <- TimeSpan.FromMinutes 20.
|
||||
opts.ExpireTimeSpan <- TimeSpan.FromMinutes 60.
|
||||
opts.SlidingExpiration <- true
|
||||
opts.AccessDeniedPath <- "/forbidden")
|
||||
let _ = builder.Services.AddLogging ()
|
||||
|
|
|
@ -56,6 +56,50 @@
|
|||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<fieldset>
|
||||
<legend>
|
||||
Metadata
|
||||
<button type="button" class="btn btn-sm btn-secondary" data-bs-toggle="collapse"
|
||||
data-bs-target="#metaItemContainer">
|
||||
show
|
||||
</button>
|
||||
</legend>
|
||||
<div id="metaItemContainer" class="collapse">
|
||||
<div id="metaItems" class="container">
|
||||
{%- for meta in metadata %}
|
||||
<div id="meta_{{ meta[0] }}" class="row mb-3">
|
||||
<div class="col-1 text-center align-self-center">
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="Admin.removeMetaItem({{ meta[0] }})">
|
||||
−
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="form-floating">
|
||||
<input type="text" name="metaNames" id="metaNames_{{ meta[0] }}" class="form-control"
|
||||
placeholder="Name" value="{{ meta[1] }}">
|
||||
<label for="metaNames_{{ meta[0] }}">Name</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="form-floating">
|
||||
<input type="text" name="metaValues" id="metaValues_{{ meta[0] }}" class="form-control"
|
||||
placeholder="Value" value="{{ meta[2] }}">
|
||||
<label for="metaValues_{{ meta[0] }}">Value</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor -%}
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-secondary" onclick="Admin.addMetaItem()">Add an Item</button>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => Admin.setNextMetaIndex({{ metadata | size }}))
|
||||
</script>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
|
98
src/MyWebLog/themes/bit-badger/home-page.liquid
Normal file
|
@ -0,0 +1,98 @@
|
|||
<div class="home">
|
||||
<article class="content auto">
|
||||
{{ page.text }}
|
||||
</article>
|
||||
<aside class="app-sidebar">
|
||||
<div>
|
||||
<div class="app-sidebar-head">Web Sites and Applications</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>PrayerTracker</strong><br>
|
||||
<a href="/solutions/prayer-tracker" title="About PrayerTracker • Bit Badger Solutions">About</a> •
|
||||
<a href="https://prayer.bitbadger.solutions" title="PrayerTracker" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">
|
||||
A prayer request tracking website (Free for any church or Sunday School class!)
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>myPrayerJournal</strong><br>
|
||||
<a href="/solutions/my-prayer-journal" title="About myPrayerJournal • Bit Badger Solutions">About</a> •
|
||||
<a href="https://prayerjournal.me" title="myPrayerJournal" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Minimalist personal prayer journal</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Linux Resources</strong><br>
|
||||
<a href="https://blog.bitbadger.solutions/linux/" title="Linux Resources" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Handy information for Linux folks</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="app-sidebar-head">WordPress</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Futility Closet</strong><br>
|
||||
<a href="/solutions/futility-closet" title="About Futility Closet • Bit Badger Solutions">About</a> •
|
||||
<a href="https://www.futilitycloset.com" title="Futility Closet" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">An idler’s miscellany of compendious amusements</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Mindy Mackenzie</strong><br>
|
||||
<a href="/solutions/mindy-mackenzie" title="About Mindy Mackenzie • Bit Badger Solutions">About</a> •
|
||||
<a href="https://mindymackenzie.com" title="Mindy Mackenzie" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description"><em>WSJ</em>-best-selling author of <em>The Courage Solution</em></p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Riehl World News</strong><br>
|
||||
<a href="/solutions/riehl-world-news" title="About Riehl World News • Bit Badger Solutions">About</a> •
|
||||
<a href="http://riehlworldview.com" title="Riehl World News" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Riehl news for real people</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="app-sidebar-head">Static Sites</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Bay Vista Baptist Church</strong><br>
|
||||
<a href="/solutions/bay-vista" title="About Bay Vista Baptist Church • Bit Badger Solutions">About</a> •
|
||||
<a href="https://bayvista.org" title="Bay Vista Baptist Church" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Biloxi, Mississippi</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>The Bit Badger Blog</strong><br>
|
||||
<a href="/solutions/tech-blog" title="About The Bit Badger Blog • Bit Badger Solutions">About</a> •
|
||||
<a href="https://blog.bitbadger.solutions" title="The Bit Badger Blog" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Technical information (“geek stuff”) from Bit Badger Solutions</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="app-sidebar-head">Personal</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>Daniel J. Summers</strong><br>
|
||||
<a href="https://daniel.summershome.org" title="Daniel J. Summers" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Daniel’s personal blog</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="app-sidebar-name">
|
||||
<strong>A Word from the Word</strong><br>
|
||||
<a href="https://devotions.summershome.org" title="A Word from the Word" target="_blank">Visit</a>
|
||||
</p>
|
||||
<p class="app-sidebar-description">Devotions by Daniel</p>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
45
src/MyWebLog/themes/bit-badger/layout.liquid
Normal file
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{ page_title }} » Bit Badger Solutions</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Oswald|Raleway">
|
||||
<link rel="stylesheet" href="/themes/{{ web_log.theme_path }}/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="header-logo">
|
||||
<a href="/">
|
||||
<img src="/themes/{{ web_log.theme_path }}/bitbadger.png"
|
||||
alt="A cartoon badger looking at a computer screen, with his paw on a mouse"
|
||||
title="Bit Badger Solutions">
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-title"><a href="/">Bit Badger Solutions</a></div>
|
||||
<div class="header-spacer"> </div>
|
||||
<div class="header-social">
|
||||
<a href="https://twitter.com/Bit_Badger" title="Bit_Badger on Twitter" target="_blank">
|
||||
<img src="/themes/{{ web_log.theme_path }}/twitter.png" alt="Twitter">
|
||||
</a>
|
||||
<a href="https://www.facebook.com/bitbadger.solutions" title="Bit Badger Solutions on Facebook" target="_blank">
|
||||
<img src="/themes/{{ web_log.theme_path }}/facebook.png" alt="Facebook">
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
{{ content }}
|
||||
<footer>
|
||||
<div>
|
||||
<small>
|
||||
{% if logged_on -%}
|
||||
<a href="/admin">Dashboard</a> ~ <a href="/user/log-off">Log Off</a>
|
||||
{% else %}
|
||||
<a href="/user/log-on">Log On</a>
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
<div>
|
||||
A <strong><a href="/">Bit Badger Solutions</a></strong> original design
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
5
src/MyWebLog/themes/bit-badger/single-page.liquid
Normal file
|
@ -0,0 +1,5 @@
|
|||
<article class="content auto">
|
||||
<h1>{{ page.title }}</h1>
|
||||
{{ page.text }}
|
||||
<p><br><a href="/" title="Home">« Home</a></p>
|
||||
</article>
|
|
@ -1,4 +1,93 @@
|
|||
const Admin = {
|
||||
/** The next index for a metadata item */
|
||||
nextMetaIndex : 0,
|
||||
|
||||
/**
|
||||
* Set the next meta item index
|
||||
* @param idx The index to set
|
||||
*/
|
||||
// Calling a function with a Liquid variable does not look like an error in the IDE...
|
||||
setNextMetaIndex(idx) {
|
||||
this.nextMetaIndex = idx
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new row for metadata entry
|
||||
*/
|
||||
addMetaItem() {
|
||||
// Remove button
|
||||
const removeBtn = document.createElement("button")
|
||||
removeBtn.type = "button"
|
||||
removeBtn.className = "btn btn-sm btn-danger"
|
||||
removeBtn.innerHTML = "−"
|
||||
removeBtn.setAttribute("onclick", `Admin.removeMetaItem(${this.nextMetaIndex})`)
|
||||
|
||||
const removeCol = document.createElement("div")
|
||||
removeCol.className = "col-1 text-center align-self-center"
|
||||
removeCol.appendChild(removeBtn)
|
||||
|
||||
// Name
|
||||
const nameField = document.createElement("input")
|
||||
nameField.type = "text"
|
||||
nameField.name = "metaNames"
|
||||
nameField.id = `metaNames_${this.nextMetaIndex}`
|
||||
nameField.className = "form-control"
|
||||
nameField.placeholder = "Name"
|
||||
|
||||
const nameLabel = document.createElement("label")
|
||||
nameLabel.htmlFor = nameField.id
|
||||
nameLabel.innerText = nameField.placeholder
|
||||
|
||||
const nameFloat = document.createElement("div")
|
||||
nameFloat.className = "form-floating"
|
||||
nameFloat.appendChild(nameField)
|
||||
nameFloat.appendChild(nameLabel)
|
||||
|
||||
const nameCol = document.createElement("div")
|
||||
nameCol.className = "col-3"
|
||||
nameCol.appendChild(nameFloat)
|
||||
|
||||
// Value
|
||||
const valueField = document.createElement("input")
|
||||
valueField.type = "text"
|
||||
valueField.name = "metaValues"
|
||||
valueField.id = `metaValues_${this.nextMetaIndex}`
|
||||
valueField.className = "form-control"
|
||||
valueField.placeholder = "Value"
|
||||
|
||||
const valueLabel = document.createElement("label")
|
||||
valueLabel.htmlFor = valueField.id
|
||||
valueLabel.innerText = valueField.placeholder
|
||||
|
||||
const valueFloat = document.createElement("div")
|
||||
valueFloat.className = "form-floating"
|
||||
valueFloat.appendChild(valueField)
|
||||
valueFloat.appendChild(valueLabel)
|
||||
|
||||
const valueCol = document.createElement("div")
|
||||
valueCol.className = "col-8"
|
||||
valueCol.appendChild(valueFloat)
|
||||
|
||||
// Put it all together
|
||||
const newRow = document.createElement("div")
|
||||
newRow.className = "row mb-3"
|
||||
newRow.id = `meta_${this.nextMetaIndex}`
|
||||
newRow.appendChild(removeCol)
|
||||
newRow.appendChild(nameCol)
|
||||
newRow.appendChild(valueCol)
|
||||
|
||||
document.getElementById("metaItems").appendChild(newRow)
|
||||
this.nextMetaIndex++
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a metadata item
|
||||
* @param idx The index of the metadata item to remove
|
||||
*/
|
||||
removeMetaItem(idx) {
|
||||
document.getElementById(`meta_${idx}`).remove()
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm and delete a category
|
||||
* @param id The ID of the category to be deleted
|
||||
|
|
BIN
src/MyWebLog/wwwroot/themes/bit-badger/bit-badger-auth.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/MyWebLog/wwwroot/themes/bit-badger/bitbadger.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
src/MyWebLog/wwwroot/themes/bit-badger/facebook.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
src/MyWebLog/wwwroot/themes/bit-badger/favicon.ico
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
src/MyWebLog/wwwroot/themes/bit-badger/screenshots/bay-vista.png
Normal file
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 23 KiB |
BIN
src/MyWebLog/wwwroot/themes/bit-badger/screenshots/nsx.png
Normal file
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 71 KiB |
BIN
src/MyWebLog/wwwroot/themes/bit-badger/screenshots/tcms.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
src/MyWebLog/wwwroot/themes/bit-badger/screenshots/tech-blog.png
Normal file
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 44 KiB |
217
src/MyWebLog/wwwroot/themes/bit-badger/style.css
Normal file
|
@ -0,0 +1,217 @@
|
|||
html {
|
||||
background-color: lightgray;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Raleway", "Segoe UI", Ubuntu, Tahoma, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||
background-color: #FFFAFA;
|
||||
}
|
||||
a {
|
||||
color: navy;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
border-bottom: dotted 1px navy;
|
||||
}
|
||||
a img {
|
||||
border: 0;
|
||||
}
|
||||
acronym {
|
||||
border-bottom: dotted 1px black;
|
||||
}
|
||||
header, h1, h2, h3, footer a {
|
||||
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 1.4rem 0;
|
||||
font-size: 2rem;
|
||||
}
|
||||
h2 {
|
||||
margin: 1.2rem 0;
|
||||
}
|
||||
h3 {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
h2, h3 {
|
||||
border-bottom: solid 2px navy;
|
||||
}
|
||||
@media all and (min-width:40rem) {
|
||||
h2, h3 {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
p {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
#content {
|
||||
margin: 0 1rem;
|
||||
}
|
||||
.content {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.auto {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media all and (min-width: 68rem) {
|
||||
.content {
|
||||
width: 66rem;
|
||||
}
|
||||
}
|
||||
.hdr {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
.strike {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.alignleft {
|
||||
float: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
ul {
|
||||
padding-left: 40px;
|
||||
}
|
||||
li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
.app-info {
|
||||
display: flex;
|
||||
flex-flow: row-reverse wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
abbr[title] {
|
||||
text-decoration: none;
|
||||
border-bottom: dotted 1px rgba(0, 0, 0, .5)
|
||||
}
|
||||
/* Page header */
|
||||
.site-header {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
background-image: linear-gradient(to bottom, lightgray, #FFFAFA);
|
||||
}
|
||||
.site-header a, .site-header a:visited {
|
||||
color: black;
|
||||
}
|
||||
.site-header a:hover {
|
||||
border-bottom: none;
|
||||
}
|
||||
.header-title {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
line-height: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.header-spacer {
|
||||
flex-grow: 3;
|
||||
}
|
||||
.header-social {
|
||||
padding: 25px .8rem 0 0;
|
||||
}
|
||||
.header-social img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
@media all and (max-width:40rem) {
|
||||
.site-header {
|
||||
height: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.header-title {
|
||||
line-height: 3rem;
|
||||
}
|
||||
.header-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/* Home page */
|
||||
@media all and (min-width: 80rem) {
|
||||
.home {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-around;
|
||||
}
|
||||
}
|
||||
.home-lead {
|
||||
font-size: 1.3rem;
|
||||
text-align: center;
|
||||
}
|
||||
.app-sidebar {
|
||||
text-align: center;
|
||||
border-top: dotted 1px lightgray;
|
||||
padding-top: 1rem;
|
||||
font-size: .9rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.app-sidebar > div {
|
||||
width: 20rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
@media all and (min-width: 68rem) {
|
||||
.app-sidebar {
|
||||
width: 66rem;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
@media all and (min-width: 80rem) {
|
||||
.app-sidebar {
|
||||
width: 12rem;
|
||||
border-top: none;
|
||||
border-left: dotted 1px lightgray;
|
||||
padding-top: 0;
|
||||
padding-left: 2rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
.app-sidebar > div {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
.app-sidebar a {
|
||||
font-size: 10pt;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.app-sidebar-head {
|
||||
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||
font-weight: bold;
|
||||
color: maroon;
|
||||
margin-bottom: .8rem;
|
||||
padding: 3px 12px;
|
||||
border-bottom: solid 2px lightgray;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.app-sidebar-name, .app-sidebar-description {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.app-sidebar-description {
|
||||
font-style: italic;
|
||||
color: #555555;
|
||||
padding-bottom: .6rem;
|
||||
}
|
||||
/* All solution page */
|
||||
.app-name {
|
||||
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: maroon;
|
||||
}
|
||||
/* Footer */
|
||||
footer {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
padding: 20px 15px 10px 15px;
|
||||
font-size: 1rem;
|
||||
color: black;
|
||||
clear: both;
|
||||
background-image: linear-gradient(to bottom, #FFFAFA, lightgray);
|
||||
}
|
||||
footer a:link, footer a:visited {
|
||||
color: black;
|
||||
}
|
BIN
src/MyWebLog/wwwroot/themes/bit-badger/twitter.png
Normal file
After Width: | Height: | Size: 10 KiB |