diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..95c4320
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.DS_Store
+/.build
+/Packages
+/*.xcodeproj
+xcuserdata/
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..706eede
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..91a8975
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Brian Dinsen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..a482468
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,21 @@
+// swift-tools-version:5.1
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "BrianPublishTheme",
+ products: [
+ .library(
+ name: "BrianPublishTheme",
+ targets: ["BrianPublishTheme"]),
+ ],
+ dependencies: [
+ .package(url: "https://github.com/johnsundell/publish.git", from: "0.2.0")
+ ],
+ targets: [
+ .target(
+ name: "BrianPublishTheme",
+ dependencies: ["Publish"]),
+ ]
+)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1c2695e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,39 @@
+# Brian theme for Publish
+
+A [Publish](https://github.com/johnsundell/publish) theme available to any Publish website.
+
+## Installation
+
+To install it into your [Publish](https://github.com/johnsundell/publish) package, add it as a dependency within your `Package.swift` manifest:
+
+```swift
+let package = Package(
+ ...
+ dependencies: [
+ ...
+ .package(url: "https://github.com/dinsen/brianpublishtheme", from: "0.1.0")
+ ],
+ targets: [
+ .target(
+ ...
+ dependencies: [
+ ...
+ "BrianPublishTheme"
+ ]
+ )
+ ]
+ ...
+)
+```
+
+For more information on how to use the Swift Package Manager, check out [this article](https://www.swiftbysundell.com/articles/managing-dependencies-using-the-swift-package-manager), or [its official documentation](https://github.com/apple/swift-package-manager/tree/master/Documentation).
+
+## Usage
+
+The theme can then be used within any publishing pipeline like this:
+
+```swift
+import BrianPublishTheme
+...
+try Brian().publish(withTheme: .brian)
+```
diff --git a/Resources/BrianTheme/styles.css b/Resources/BrianTheme/styles.css
new file mode 100644
index 0000000..e3ab363
--- /dev/null
+++ b/Resources/BrianTheme/styles.css
@@ -0,0 +1,218 @@
+/**
+* Publish Brian theme
+* Copyright (c) Brian Dinsen 2020
+* MIT license, see LICENSE file for details
+* This file is a modified version of
+* Foundation theme included in Publish
+*/
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ background: #fff;
+ color: #000;
+ font-family: Helvetica, Arial;
+ text-align: center;
+}
+
+.wrapper {
+ max-width: 900px;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 40px;
+ text-align: left;
+}
+
+header {
+ background-color: #f44336;
+}
+
+header .wrapper {
+ padding: 128px 16px;
+ text-align: center;
+}
+
+header a {
+ text-decoration: none;
+}
+
+header .site-name {
+ font-size: 1.5em;
+ color: #000;
+ font-weight: bold;
+}
+
+nav {
+ margin-top: 20px;
+}
+
+nav li {
+ display: inline-block;
+ padding: 5px;
+}
+
+h1 {
+ margin-bottom: 20px;
+ font-size: 2em;
+}
+
+h2 {
+ margin: 20px 0;
+}
+
+p {
+ margin-bottom: 10px;
+}
+
+a {
+ color: inherit;
+}
+
+.description {
+ margin-bottom: 40px;
+}
+
+.item-list > li {
+ display: block;
+ padding: 20px;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ border-radius: 20px;
+ background-color: #eee;
+}
+
+.item-list h1 {
+ margin-bottom: 15px;
+ font-size: 1.3em;
+}
+
+.item-list p {
+ margin-bottom: 0;
+}
+
+.tag-list {
+ margin-bottom: 15px;
+}
+
+.tag-list li,
+.tag {
+ display: inline-block;
+ background-color: #000;
+ color: #ddd;
+ padding: 4px 6px;
+ border-radius: 5px;
+ margin-right: 5px;
+}
+
+.tag-list a,
+.tag a {
+ text-decoration: none;
+}
+
+.item-page .tag-list {
+ display: inline-block;
+}
+
+.content {
+ margin-bottom: 40px;
+ line-height: 1.5em;
+}
+
+.browse-all {
+ display: block;
+ margin-bottom: 30px;
+}
+
+.all-tags li {
+ font-size: 1.4em;
+ margin-right: 10px;
+ padding: 6px 10px;
+}
+
+footer {
+ color: #8a8a8a;
+}
+
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #222;
+ }
+
+ body,
+ header .site-name {
+ color: #fff;
+ }
+
+ .item-list > li {
+ background-color: #333;
+ }
+
+ header {
+ background-color: #f44336;
+ }
+}
+
+/**
+ * Example CSS file that can be used to style Splash HTML output
+ * Copyright (c) John Sundell 2018
+ * MIT license - see LICENSE.md
+ */
+
+pre {
+ margin-bottom: 1.5em;
+ background-color: #1a1a1a;
+ padding: 16px 0;
+ border-radius: 16px;
+}
+
+pre code {
+ font-family: monospace;
+ display: block;
+ padding: 0 20px;
+ color: #a9bcbc;
+ line-height: 1.4em;
+ font-size: 0.95em;
+ overflow-x: auto;
+ white-space: pre;
+ -webkit-overflow-scrolling: touch;
+}
+
+pre code .keyword {
+ color: #e73289;
+}
+
+pre code .type {
+ color: #8281ca;
+}
+
+pre code .call {
+ color: #348fe5;
+}
+
+pre code .property {
+ color: #21ab9d;
+}
+
+pre code .number {
+ color: #db6f57;
+}
+
+pre code .string {
+ color: #fa641e;
+}
+
+pre code .comment {
+ color: #6b8a94;
+}
+
+pre code .dotAccess {
+ color: #92b300;
+}
+
+pre code .preprocessing {
+ color: #b68a00;
+}
diff --git a/Sources/BrianPublishTheme/Theme+Brian.swift b/Sources/BrianPublishTheme/Theme+Brian.swift
new file mode 100644
index 0000000..5aac0f4
--- /dev/null
+++ b/Sources/BrianPublishTheme/Theme+Brian.swift
@@ -0,0 +1,249 @@
+/**
+* BrianPublishTheme
+* Copyright (c) Brian Dinsen 2020
+* MIT license, see LICENSE file for details
+* This file is a modified version of
+* Foundation theme included in Publish
+*/
+
+import Plot
+import Publish
+
+public extension Theme {
+
+ static var brian: Self {
+ Theme(
+ htmlFactory: BrianHTMLFactory(),
+ resourcePaths: ["Resources/BrianTheme/styles.css"]
+ )
+ }
+}
+
+private struct BrianHTMLFactory: HTMLFactory {
+ func makeIndexHTML(for index: Index,
+ context: PublishingContext) throws -> HTML {
+ HTML(
+ .lang(context.site.language),
+ .head(for: index, on: context.site),
+ .body(
+ .header(for: context, selectedSection: nil),
+ .wrapper(
+ .h1(.text(index.title)),
+ .p(
+ .class("description"),
+ .text(context.site.description)
+ ),
+ .h2("Latest content"),
+ .itemList(
+ for: context.allItems(
+ sortedBy: \.date,
+ order: .descending
+ ),
+ on: context.site
+ )
+ ),
+ .footer(for: context.site)
+ )
+ )
+ }
+
+ func makeSectionHTML(for section: Section,
+ context: PublishingContext) throws -> HTML {
+ HTML(
+ .lang(context.site.language),
+ .head(for: section, on: context.site),
+ .body(
+ .header(for: context, selectedSection: section.id),
+ .wrapper(
+ .h1(.text(section.title)),
+ .itemList(for: section.items, on: context.site)
+ ),
+ .footer(for: context.site)
+ )
+ )
+ }
+
+ func makeItemHTML(for item: Item,
+ context: PublishingContext) throws -> HTML {
+ HTML(
+ .lang(context.site.language),
+ .head(for: item, on: context.site),
+ .body(
+ .class("item-page"),
+ .header(for: context, selectedSection: item.sectionID),
+ .wrapper(
+ .article(
+ .div(
+ .class("content"),
+ .contentBody(item.body)
+ ),
+ .span("Tagged with: "),
+ .tagList(for: item, on: context.site)
+ )
+ ),
+ .footer(for: context.site)
+ )
+ )
+ }
+
+ func makePageHTML(for page: Page,
+ context: PublishingContext) throws -> HTML {
+ HTML(
+ .lang(context.site.language),
+ .head(for: page, on: context.site),
+ .body(
+ .header(for: context, selectedSection: nil),
+ .wrapper(
+
+ ),
+ .footer(for: context.site)
+ )
+ )
+ }
+
+ func makeTagListHTML(for page: TagListPage,
+ context: PublishingContext) throws -> HTML? {
+ HTML(
+ .lang(context.site.language),
+ .head(for: page, on: context.site),
+ .body(
+ .header(for: context, selectedSection: nil),
+ .wrapper(
+ .h1("Browse all tags"),
+ .ul(
+ .class("all-tags"),
+ .forEach(page.tags.sorted()) { tag in
+ .li(
+ .class("tag"),
+ .a(
+ .href(context.site.path(for: tag)),
+ .text(tag.string)
+ )
+ )
+ }
+ )
+ ),
+ .footer(for: context.site)
+ )
+ )
+ }
+
+ func makeTagDetailsHTML(for page: TagDetailsPage,
+ context: PublishingContext) throws -> HTML? {
+ HTML(
+ .lang(context.site.language),
+ .head(for: page, on: context.site),
+ .body(
+ .header(for: context, selectedSection: nil),
+ .wrapper(
+ .h1(
+ "Tagged with ",
+ .span(.class("tag"), .text(page.tag.string))
+ ),
+ .a(
+ .class("browse-all"),
+ .text("Browse all tags"),
+ .href(context.site.tagListPath)
+ ),
+ .itemList(
+ for: context.items(
+ taggedWith: page.tag,
+ sortedBy: \.date,
+ order: .descending
+ ),
+ on: context.site
+ )
+ ),
+ .footer(for: context.site)
+ )
+ )
+ }
+}
+
+private extension Node where Context == HTML.BodyContext {
+ static func wrapper(_ nodes: Node...) -> Node {
+ .div(.class("wrapper"), .group(nodes))
+ }
+
+ static func header(
+ for context: PublishingContext,
+ selectedSection: T.SectionID?
+ ) -> Node {
+ let sectionIDs = T.SectionID.allCases
+
+ return .header(
+ .wrapper(
+ .a(.class("site-name"), .href("/"), .text(context.site.name)),
+ .if(sectionIDs.count > 1,
+ .nav(
+ .ul(.forEach(sectionIDs) { section in
+ .li(
+ .a(
+ .class(section == selectedSection ? "selected" : ""),
+ .href(context.sections[section].path),
+ .text(context.sections[section].title)
+ )
+ )
+ })
+ )
+ )
+ )
+ )
+ }
+
+ static func itemList(for items: [Item], on site: T) -> Node {
+ return .ul(
+ .class("item-list"),
+ .forEach(items) { item in
+ .li(.article(
+ .h1(.a(
+ .href(item.path),
+ .text(item.title)
+ )),
+ .tagList(for: item, on: site),
+ .p(.text(item.description))
+ ))
+ }
+ )
+ }
+
+ static func tagList(for item: Item, on site: T) -> Node {
+ return .ul(.class("tag-list"), .forEach(item.tags) { tag in
+ .li(.a(
+ .href(site.path(for: tag)),
+ .text(tag.string)
+ ))
+ })
+ }
+
+ static func footer(for site: T) -> Node {
+ return .footer(
+ .p(
+ .text("Generated with ❤️ using "),
+ .a(
+ .text("Publish"),
+ .href("https://github.com/johnsundell/publish")
+ )
+ ),
+ .p(
+ .a(
+ .text("RSS feed"),
+ .href("/feed.rss")
+ ),
+ .text(" | "),
+ .a(
+ .text("Twitter"),
+ .href("https://twitter.com/briandinsen"),
+ .target(.blank)
+ ),
+ .text(" | "),
+ .a(
+ .text("GitHub"),
+ .href("https://github.com/dinsen"),
+ .target(.blank)
+ )
+ )
+ )
+ }
+}
+
diff --git a/Tests/BrianPublishThemeTests/BrianPublishThemeTests.swift b/Tests/BrianPublishThemeTests/BrianPublishThemeTests.swift
new file mode 100644
index 0000000..aa58a41
--- /dev/null
+++ b/Tests/BrianPublishThemeTests/BrianPublishThemeTests.swift
@@ -0,0 +1,6 @@
+import XCTest
+@testable import BrianPublishTheme
+
+final class BrianPublishThemeTests: XCTestCase {
+
+}
diff --git a/Tests/BrianPublishThemeTests/XCTestManifests.swift b/Tests/BrianPublishThemeTests/XCTestManifests.swift
new file mode 100644
index 0000000..14d3d57
--- /dev/null
+++ b/Tests/BrianPublishThemeTests/XCTestManifests.swift
@@ -0,0 +1,2 @@
+import XCTest
+
diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift
new file mode 100644
index 0000000..ef772b7
--- /dev/null
+++ b/Tests/LinuxMain.swift
@@ -0,0 +1,7 @@
+import XCTest
+
+import BrianPublishThemeTests
+
+//var tests = [XCTestCaseEntry]()
+//tests += BrianPublishThemeTests.allTests()
+//XCTMain(tests)