Skip to content

Commit 9f3182a

Browse files
committed
wip: remove alpinejs, restructure aria, restructure styling
1 parent 04dc020 commit 9f3182a

File tree

6 files changed

+291
-265
lines changed

6 files changed

+291
-265
lines changed

Sources/Pages/HomePage.swift

Lines changed: 141 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,151 @@ import Elementary
55
import Foundation
66

77
public struct HomePage: Page {
8+
@Dependency(\.activityClient) private var activityClient
9+
10+
public init() {}
11+
12+
private static let copyrightDateFormatter = {
13+
let formatter = DateFormatter()
14+
formatter.locale = Locale(languageCode: .english, languageRegion: .unitedStates)
15+
formatter.timeZone = TimeZone(abbreviation: "PST") ?? formatter.timeZone
16+
formatter.dateFormat = "yyyy"
17+
return formatter
18+
}()
19+
20+
let styling = Style()
21+
22+
public var content: some HTML {
23+
HTMLRaw("<!DOCTYPE html>")
24+
html(.lang("en"), .custom(name: "data-theme", value: "dark")) {
25+
head {
26+
title { "Erik Bautista Santibanez" }
27+
meta(.charset(.utf8))
28+
meta(name: .viewport, content: "width=device-width, initial-scale=1.0")
29+
link(.rel(.stylesheet), .href("https://cdnjs.cloudflare.com/ajax/libs/modern-normalize/3.0.1/modern-normalize.min.css"))
30+
style(self.styling)
31+
script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"))
32+
script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/swift.min.js"))
33+
script { HTMLRaw("hljs.highlightAll();") }
34+
VueScript()
35+
}
36+
body {
37+
header(.class("hero"), .aria.label("About")) {
38+
hgroup {
39+
h1(.class("hero-title")) { "Erik Bautista Santibanez" }
40+
p(.class("hero-subtitle")) { "Swift & Web Developer" }
41+
42+
let location = self.activityClient.location()
43+
let residency = location?.residency ?? .default
44+
45+
p(.class("hero-location")) {
46+
span(.aria.label("Residency")) {
47+
svg(.xmlns(), .fill("currentColor"), .viewBox("0 0 256 256"), .class("svg-icon"), .aria.label("Map pin icon")) {
48+
path(
49+
.d("M128,16a88.1,88.1,0,0,0-88,88c0,75.3,80,132.17,83.41,134.55a8,8,0,0,0,9.18,0C136,236.17,216,179.3,216,104A88.1,88.1,0,0,0,128,16Zm0,56a32,32,0,1,1-32,32A32,32,0,0,1,128,72Z")
50+
)
51+
}
52+
"\(residency)"
53+
}
54+
55+
if let location, location.city != residency.city || location.state != residency.state {
56+
" \u{2022} "
57+
58+
span(.aria.label("Location")) {
59+
svg(.xmlns(), .fill("currentColor"), .viewBox("0 0 256 256"), .class("svg-icon reversed"), .aria.label("Navigation icon")) {
60+
path(.d("M234.35,129,152,152,129,234.35a8,8,0,0,1-15.21.27l-65.28-176A8,8,0,0,1,58.63,48.46l176,65.28A8,8,0,0,1,234.35,129Z"))
61+
path(.d("M237.33,106.21,61.41,41l-.16-.05A16,16,0,0,0,40.9,61.25a1,1,0,0,0,.05.16l65.26,175.92A15.77,15.77,0,0,0,121.28,248h.3a15.77,15.77,0,0,0,15-11.29l.06-.2,21.84-78,78-21.84.2-.06a16,16,0,0,0,.62-30.38ZM149.84,144.3a8,8,0,0,0-5.54,5.54L121.3,232l-.06-.17L56,56l175.82,65.22.16.06Z"))
62+
}
63+
64+
"Currently in "
65+
66+
b {
67+
[location.city, location.state, location.region == "United States" ? nil : location.region]
68+
.compactMap(\.self)
69+
.joined(separator: ", ")
70+
}
71+
}
72+
}
73+
}
74+
}
75+
}
76+
main(.v.scope("{ selection: undefined }")) {
77+
header {
78+
hgroup {
79+
h2 { "Posts" }
80+
ul(.class("post-tabs")) {
81+
for kind in Post.Kind?.allCases {
82+
let value = if let kind {
83+
"'\(kind.rawValue)'"
84+
} else {
85+
"undefined"
86+
}
87+
li {
88+
button(
89+
.v.on(.click, "selection = \(value)"),
90+
.v.bind("aria-selected", "selection == \(value)"),
91+
.aria.selected(kind == nil)
92+
) {
93+
kind?.tabTitle ?? "All"
94+
}
95+
}
96+
}
97+
}
98+
}
99+
}
100+
section {
101+
for post in Post.allCases {
102+
article(
103+
.class("post"),
104+
.v.show("!selection || selection == '\(post.kind.rawValue)'")
105+
) {
106+
header { post.dateFormatted }
107+
h3(.class("post-title")) { post.title }
108+
div(.class("post-content")) { post.content }
109+
}
110+
}
111+
}
112+
}
113+
footer(.aria.label("Credits")) {
114+
hr()
115+
p { "©\(Self.copyrightDateFormatter.string(from: Date.now)) Erik Bautista Santibanez" }
116+
p {
117+
"Made with \u{2764} using "
118+
a(.target(.blank), .rel("noopener noreferrer"), .href("https://swift.org")) { "Swift" }
119+
" + "
120+
a(.target(.blank), .rel("noopener noreferrer"), .href("https://hummingbird.codes")) { "Hummingbird" }
121+
"."
122+
}
123+
}
124+
}
125+
}
126+
}
127+
}
128+
129+
extension HomePage {
8130
struct Style: @unchecked Sendable, StyleSheet {
9131
var body: some Rule {
10132
// TODO: Add Work-Sans font?
11133

134+
// Reset components
135+
":root" => {
136+
AnyProperty("line-height", "1.5")
137+
}
138+
139+
"h1, h2, h3, h4, h5, figure, p, ol, ul, pre" => {
140+
AnyProperty("margin", "0")
141+
}
142+
143+
"ol[role=\"list\"], ul[role=\"list\"]" => {
144+
AnyProperty("list-style", "none")
145+
AnyProperty("padding-inline", "0")
146+
}
147+
148+
Element(.img) => {
149+
AnyProperty("display", "block")
150+
AnyProperty("max-inline-size", "100%")
151+
}
152+
12153
// General
13154
Pseudo(class: .root) => {
14155
BackgroundColor("#1c1c1c")
@@ -122,148 +263,4 @@ public struct HomePage: Page {
122263
}
123264
}
124265
}
125-
126-
@Dependency(\.activityClient) private var activityClient
127-
128-
public init() {}
129-
130-
private static let copyrightDateFormatter = {
131-
let formatter = DateFormatter()
132-
formatter.locale = Locale(languageCode: .english, languageRegion: .unitedStates)
133-
formatter.timeZone = TimeZone(abbreviation: "PST") ?? formatter.timeZone
134-
formatter.dateFormat = "yyyy"
135-
return formatter
136-
}()
137-
138-
let styling = Style()
139-
140-
public var content: some HTML {
141-
HTMLRaw("<!DOCTYPE html>")
142-
html(.lang("en"), .custom(name: "data-theme", value: "dark")) {
143-
head {
144-
title { "Erik Bautista Santibanez" }
145-
meta(.charset(.utf8))
146-
meta(name: .viewport, content: "width=device-width, initial-scale=1.0")
147-
link(.rel(.stylesheet), .href("https://cdnjs.cloudflare.com/ajax/libs/modern-normalize/3.0.1/modern-normalize.min.css"))
148-
style {
149-
/// Reset stylesheet
150-
HTMLRaw(
151-
"""
152-
:root {
153-
line-height: 1.5;
154-
}
155-
h1, h2, h3, h4, h5, figure, p, ol, ul, pre {
156-
margin: 0;
157-
}
158-
ol[role="list"], ul[role="list"] {
159-
list-style: none;
160-
padding-inline: 0;
161-
}
162-
img {
163-
display: block;
164-
max-inline-size: 100%;
165-
}
166-
"""
167-
)
168-
}
169-
style(self.styling)
170-
script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"))
171-
script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/swift.min.js"))
172-
script { HTMLRaw("hljs.highlightAll();") }
173-
VueScript()
174-
}
175-
body {
176-
header(.class("hero"), .ariaLabel("About")) {
177-
hgroup {
178-
h1(.class("hero-title")) { "Erik Bautista Santibanez" }
179-
p(.class("hero-subtitle")) { "Swift & Web Developer" }
180-
181-
let location = self.activityClient.location()
182-
let residency = location?.residency ?? .default
183-
184-
p(.class("hero-location")) {
185-
span(.ariaLabel("Residency")) {
186-
svg(.xmlns(), .fill("currentColor"), .viewBox("0 0 256 256"), .class("svg-icon"), .ariaLabel("Map pin icon")) {
187-
path(
188-
.d("M128,16a88.1,88.1,0,0,0-88,88c0,75.3,80,132.17,83.41,134.55a8,8,0,0,0,9.18,0C136,236.17,216,179.3,216,104A88.1,88.1,0,0,0,128,16Zm0,56a32,32,0,1,1-32,32A32,32,0,0,1,128,72Z")
189-
)
190-
}
191-
"\(residency)"
192-
}
193-
194-
if let location, location.city != residency.city || location.state != residency.state {
195-
" \u{2022} "
196-
197-
span(.ariaLabel("Location")) {
198-
svg(.xmlns(), .fill("currentColor"), .viewBox("0 0 256 256"), .class("svg-icon reversed"), .ariaLabel("Navigation icon")) {
199-
path(.d("M234.35,129,152,152,129,234.35a8,8,0,0,1-15.21.27l-65.28-176A8,8,0,0,1,58.63,48.46l176,65.28A8,8,0,0,1,234.35,129Z"))
200-
path(.d("M237.33,106.21,61.41,41l-.16-.05A16,16,0,0,0,40.9,61.25a1,1,0,0,0,.05.16l65.26,175.92A15.77,15.77,0,0,0,121.28,248h.3a15.77,15.77,0,0,0,15-11.29l.06-.2,21.84-78,78-21.84.2-.06a16,16,0,0,0,.62-30.38ZM149.84,144.3a8,8,0,0,0-5.54,5.54L121.3,232l-.06-.17L56,56l175.82,65.22.16.06Z"))
201-
}
202-
203-
"Currently in "
204-
205-
b {
206-
[location.city, location.state, location.region == "United States" ? nil : location.region]
207-
.compactMap(\.self)
208-
.joined(separator: ", ")
209-
}
210-
}
211-
}
212-
}
213-
}
214-
}
215-
main(.v.scope("{ selection: undefined }")) {
216-
header {
217-
hgroup {
218-
h2 { "Posts" }
219-
ul(.class("post-tabs")) {
220-
for kind in Post.Kind?.allCases {
221-
let value = if let kind {
222-
"'\(kind.rawValue)'"
223-
} else {
224-
"undefined"
225-
}
226-
li {
227-
button(
228-
.v.on(.click, "selection = \(value)"),
229-
.v.bind("aria-selected", "selection == \(value)"),
230-
.ariaSelected(kind == nil)
231-
) {
232-
kind?.tabTitle ?? "All"
233-
}
234-
}
235-
}
236-
}
237-
}
238-
// p { "Selected {{ selection }}" }
239-
}
240-
section {
241-
for post in Post.allCases {
242-
article(
243-
.class("post"),
244-
.v.show("!selection || selection == '\(post.kind.rawValue)'")
245-
) {
246-
header { post.dateFormatted }
247-
h3(.class("post-title")) { post.title }
248-
div(.class("post-content")) {
249-
post.content
250-
}
251-
}
252-
}
253-
}
254-
}
255-
footer(.ariaLabel("Credits")) {
256-
hr()
257-
p { "©\(Self.copyrightDateFormatter.string(from: Date.now)) Erik Bautista Santibanez" }
258-
p {
259-
"Made with \u{2764} using "
260-
a(.target(.blank), .rel("noopener noreferrer"), .href("https://swift.org")) { "Swift" }
261-
" + "
262-
a(.target(.blank), .rel("noopener noreferrer"), .href("https://hummingbird.codes")) { "Hummingbird" }
263-
"."
264-
}
265-
}
266-
}
267-
}
268-
}
269266
}

Sources/Pages/Utils/Elementary+.swift

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,4 @@ extension meta {
1515
init(name: HTMLAttribute<Tag>.Name, content: String) {
1616
self.init(.name(name), .content(content))
1717
}
18-
}
19-
20-
public typealias SendableHTML = HTML & Sendable
21-
22-
// Aria-based attributes
23-
extension HTMLAttribute {
24-
static func ariaLabel(_ value: String) -> Self {
25-
HTMLAttribute(name: "aria-label", value: value)
26-
}
27-
28-
static func ariaCurrent(_ value: String) -> Self {
29-
HTMLAttribute(name: "aria-current", value: value)
30-
}
31-
32-
static func ariaSelected(_ value: Bool?) -> Self {
33-
HTMLAttribute(name: "aria-selected", value: value?.description)
34-
}
3518
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Elementary
2+
3+
extension HTMLAttribute where Tag: HTMLTrait.Attributes.Global {
4+
/// A namespace for Aria attributes.
5+
/// See the [Aria attributes](https://vuejs.org/api/built-in-directives) for more information.
6+
enum aria {}
7+
}
8+
9+
extension HTMLAttribute.aria {
10+
static func label(_ value: String) -> HTMLAttribute {
11+
aria(value)
12+
}
13+
14+
static func current(_ value: String) -> HTMLAttribute {
15+
aria(value)
16+
}
17+
18+
static func selected(_ value: Bool?) -> HTMLAttribute {
19+
aria(value?.description)
20+
}
21+
22+
private static func aria(
23+
name: String = #function,
24+
_ value: String?
25+
) -> HTMLAttribute {
26+
.init(name: "aria-\(name.components(separatedBy: "(").first ?? name)", value: value)
27+
}
28+
}

Sources/Pages/Utils/Elementary+Markdown.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private struct HTMLMarkdownConverter: MarkupVisitor {
119119
mutating func visitImage(_ image: Image) -> AnyHTML {
120120
if let source = image.source {
121121
a(.href(source), .target(.blank), .rel("noopener noreferrer")) {
122-
img(.src(source), .ariaLabel(image.title ?? ""))
122+
img(.src(source), .aria.label(image.title ?? ""))
123123
}
124124
}
125125
}
@@ -148,7 +148,7 @@ private struct HTMLMarkdownConverter: MarkupVisitor {
148148
let attributes: [HTMLAttribute<HTMLTag.a>] = [
149149
.href(href),
150150
link.title.flatMap { .title($0) },
151-
link.title.flatMap { .ariaLabel($0) },
151+
link.title.flatMap { .aria.label($0) },
152152
isLocalLink ? nil : .target(.blank),
153153
isLocalLink ? nil : .rel("noopener noreferrer")
154154
]

0 commit comments

Comments
 (0)