@zvndev/darkmown 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +70 -1
- package/docs/spec-alignment.md +14 -0
- package/package.json +1 -1
- package/site/_/base.skin +231 -18
- package/site/_/nav.wd +1 -1
- package/site/pages/app.skin +27 -5
- package/site/pages/blog/_schema.wd +15 -0
- package/site/pages/blog/collections-and-pagination.md +38 -0
- package/site/pages/blog/drafts-that-never-leak.md +2 -0
- package/site/pages/blog/index.skin +144 -0
- package/site/pages/blog/index.wd +73 -0
- package/site/pages/blog/shipping-feeds.md +1 -0
- package/site/pages/blog/typed-frontmatter.md +45 -0
- package/site/pages/blog/zero-js-by-default.md +31 -0
- package/site/pages/data.skin +50 -9
- package/site/pages/docs/index.wd +47 -0
- package/site/pages/loops.skin +43 -6
- package/site/pages/reactive.skin +38 -6
- package/site/pages/store.skin +43 -6
- package/src/builder.js +137 -29
- package/src/compiler/collections.js +316 -0
- package/src/compiler/context.js +28 -1
- package/src/compiler/loops.js +130 -12
- package/src/compiler/page.js +74 -21
- package/src/templates/blog/site/pages/index.skin +84 -0
- package/src/templates/dashboard/site/pages/index.skin +84 -0
- package/src/templates/landing/site/pages/index.skin +113 -6
- package/src/templates/starter/site/pages/index.skin +81 -3
- package/src/templates/store/site/pages/index.skin +93 -11
- package/site/pages/blog/index.md +0 -13
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* The Darkmown blog index — a collection-driven, paginated listing. Thin layer
|
|
2
|
+
over /base.wd (tokens, typography, dark mode); only the post-card grid and the
|
|
3
|
+
pager live here. Namespaced under .app-blog so it can't leak. */
|
|
4
|
+
|
|
5
|
+
.app-blog
|
|
6
|
+
max-width 52rem
|
|
7
|
+
|
|
8
|
+
.app-blog .blog-head
|
|
9
|
+
margin 2.5rem 0 2rem
|
|
10
|
+
|
|
11
|
+
.app-blog .blog-head .eyebrow
|
|
12
|
+
display inline-block
|
|
13
|
+
font-size .78rem
|
|
14
|
+
font-weight 600
|
|
15
|
+
text-transform uppercase
|
|
16
|
+
letter-spacing .12em
|
|
17
|
+
color var(--accent)
|
|
18
|
+
margin-bottom .6rem
|
|
19
|
+
|
|
20
|
+
.app-blog .blog-head h1
|
|
21
|
+
margin .2rem 0 .8rem
|
|
22
|
+
line-height 1.1
|
|
23
|
+
|
|
24
|
+
.app-blog .blog-head p
|
|
25
|
+
color var(--muted)
|
|
26
|
+
max-width 38rem
|
|
27
|
+
|
|
28
|
+
/* The list of posts. */
|
|
29
|
+
.app-blog .post-list
|
|
30
|
+
display flex
|
|
31
|
+
flex-direction column
|
|
32
|
+
gap 1.1rem
|
|
33
|
+
margin 2rem 0
|
|
34
|
+
|
|
35
|
+
.app-blog .post-card
|
|
36
|
+
background var(--panel)
|
|
37
|
+
border 1px solid var(--line)
|
|
38
|
+
radius var(--radius-lg)
|
|
39
|
+
padding 1.5rem 1.6rem
|
|
40
|
+
transition border-color .15s ease, transform .15s ease, box-shadow .15s ease
|
|
41
|
+
|
|
42
|
+
.app-blog .post-card:hover
|
|
43
|
+
border-color var(--accent)
|
|
44
|
+
transform translateY(-2px)
|
|
45
|
+
box-shadow 0 8px 24px -16px var(--accent-deep)
|
|
46
|
+
|
|
47
|
+
.app-blog .post-meta
|
|
48
|
+
display flex
|
|
49
|
+
align-items center
|
|
50
|
+
flex-wrap wrap
|
|
51
|
+
gap .55rem
|
|
52
|
+
margin-bottom .55rem
|
|
53
|
+
|
|
54
|
+
/* Each meta item (time / tag) renders as its own paragraph; strip the default
|
|
55
|
+
block margins so they sit inline in the flex row. */
|
|
56
|
+
.app-blog .post-meta p
|
|
57
|
+
margin 0
|
|
58
|
+
display inline-flex
|
|
59
|
+
|
|
60
|
+
.app-blog .post-meta time
|
|
61
|
+
font-size .82rem
|
|
62
|
+
font-weight 600
|
|
63
|
+
color var(--muted)
|
|
64
|
+
font-variant-numeric tabular-nums
|
|
65
|
+
|
|
66
|
+
.app-blog .tag
|
|
67
|
+
font-size .72rem
|
|
68
|
+
font-weight 600
|
|
69
|
+
text-transform uppercase
|
|
70
|
+
letter-spacing .06em
|
|
71
|
+
color var(--accent-deep)
|
|
72
|
+
background var(--accent-soft)
|
|
73
|
+
border 1px solid color-mix(in srgb, var(--accent) 30%, transparent)
|
|
74
|
+
padding .15rem .5rem
|
|
75
|
+
radius 999px
|
|
76
|
+
|
|
77
|
+
.app-blog .post-card h3
|
|
78
|
+
margin .1rem 0 .5rem
|
|
79
|
+
font-size 1.4rem
|
|
80
|
+
line-height 1.2
|
|
81
|
+
|
|
82
|
+
.app-blog .post-card h3 a
|
|
83
|
+
color var(--ink)
|
|
84
|
+
text-decoration none
|
|
85
|
+
|
|
86
|
+
.app-blog .post-card h3 a:hover
|
|
87
|
+
color var(--accent)
|
|
88
|
+
|
|
89
|
+
.app-blog .post-excerpt
|
|
90
|
+
color var(--ink-soft)
|
|
91
|
+
margin 0 0 .8rem
|
|
92
|
+
line-height 1.55
|
|
93
|
+
|
|
94
|
+
.app-blog .post-more a
|
|
95
|
+
font-size .88rem
|
|
96
|
+
font-weight 600
|
|
97
|
+
color var(--accent)
|
|
98
|
+
text-decoration none
|
|
99
|
+
|
|
100
|
+
.app-blog .post-more a:hover
|
|
101
|
+
text-decoration underline
|
|
102
|
+
|
|
103
|
+
.app-blog .empty-note
|
|
104
|
+
color var(--muted)
|
|
105
|
+
font-style italic
|
|
106
|
+
padding 2rem
|
|
107
|
+
text-align center
|
|
108
|
+
border 1px dashed var(--line)
|
|
109
|
+
radius var(--radius)
|
|
110
|
+
|
|
111
|
+
/* The pager — plain links to the generated routes, zero JS. */
|
|
112
|
+
.app-blog .pager
|
|
113
|
+
display flex
|
|
114
|
+
align-items center
|
|
115
|
+
justify-content space-between
|
|
116
|
+
gap 1rem
|
|
117
|
+
margin 2.5rem 0 3rem
|
|
118
|
+
padding-top 1.4rem
|
|
119
|
+
border-top 1px solid var(--line)
|
|
120
|
+
|
|
121
|
+
.app-blog .pager-side
|
|
122
|
+
flex 1
|
|
123
|
+
min-width 0
|
|
124
|
+
|
|
125
|
+
.app-blog .pager-side p
|
|
126
|
+
margin 0
|
|
127
|
+
|
|
128
|
+
.app-blog .pager-side--end
|
|
129
|
+
text-align right
|
|
130
|
+
|
|
131
|
+
.app-blog .pager-now
|
|
132
|
+
font-size .85rem
|
|
133
|
+
font-weight 600
|
|
134
|
+
color var(--muted)
|
|
135
|
+
white-space nowrap
|
|
136
|
+
font-variant-numeric tabular-nums
|
|
137
|
+
|
|
138
|
+
.app-blog .pager-link
|
|
139
|
+
font-weight 600
|
|
140
|
+
color var(--accent)
|
|
141
|
+
text-decoration none
|
|
142
|
+
|
|
143
|
+
.app-blog .pager-link:hover
|
|
144
|
+
text-decoration underline
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The Darkmown Blog
|
|
3
|
+
description: Notes on shipping Markdown that runs — collections, feeds, drafts, and zero-JS releases. This whole index is one @loop over a folder.
|
|
4
|
+
image: https://darkmown.com/__wd/media/og.png
|
|
5
|
+
transitions: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
@include /nav.wd
|
|
9
|
+
@include /base.wd
|
|
10
|
+
|
|
11
|
+
<main class="wide app-blog">
|
|
12
|
+
|
|
13
|
+
<header class="blog-head">
|
|
14
|
+
<span class="eyebrow">The Darkmown Blog</span>
|
|
15
|
+
|
|
16
|
+
# Notes from the framework
|
|
17
|
+
|
|
18
|
+
Every post below is a plain `.md` file in `site/pages/blog/`. This page is **one
|
|
19
|
+
`@loop` over that folder** — sorted newest-first, paginated, and 100% static
|
|
20
|
+
HTML. No hand-maintained list, no config, no JavaScript.
|
|
21
|
+
|
|
22
|
+
</header>
|
|
23
|
+
|
|
24
|
+
<section class="post-list">
|
|
25
|
+
|
|
26
|
+
@loop blog into post sort by post.date desc paginate 3
|
|
27
|
+
::: card .post-card
|
|
28
|
+
::: post-meta
|
|
29
|
+
<time>{ post.date }</time>
|
|
30
|
+
@loop post.tags into tag
|
|
31
|
+
<span class="tag">{ tag }</span>
|
|
32
|
+
@endloop
|
|
33
|
+
:::
|
|
34
|
+
|
|
35
|
+
### [{ post.title }]({ post.url })
|
|
36
|
+
|
|
37
|
+
::: post-excerpt
|
|
38
|
+
{ post.excerpt }
|
|
39
|
+
:::
|
|
40
|
+
|
|
41
|
+
::: post-more
|
|
42
|
+
[Read post → ]({ post.url })
|
|
43
|
+
:::
|
|
44
|
+
:::
|
|
45
|
+
@empty
|
|
46
|
+
::: empty-note
|
|
47
|
+
No posts yet. Add a dated `.md` file to `site/pages/blog/`.
|
|
48
|
+
:::
|
|
49
|
+
@endloop
|
|
50
|
+
|
|
51
|
+
</section>
|
|
52
|
+
|
|
53
|
+
::: pager
|
|
54
|
+
::: pager-side
|
|
55
|
+
:if page.prev
|
|
56
|
+
[← Newer posts]({ page.prev }){.pager-link}
|
|
57
|
+
:endif
|
|
58
|
+
:::
|
|
59
|
+
|
|
60
|
+
::: pager-now
|
|
61
|
+
Page { page.current } of { page.total }
|
|
62
|
+
:::
|
|
63
|
+
|
|
64
|
+
::: pager-side .pager-side--end
|
|
65
|
+
:if page.next
|
|
66
|
+
[Older posts →]({ page.next }){.pager-link}
|
|
67
|
+
:endif
|
|
68
|
+
:::
|
|
69
|
+
:::
|
|
70
|
+
|
|
71
|
+
</main>
|
|
72
|
+
|
|
73
|
+
@include /footer.wd
|
|
@@ -3,6 +3,7 @@ title: Shipping feeds without a config file
|
|
|
3
3
|
description: Site identity lives in the home page frontmatter — one site_url field turns on sitemap.xml and rss.xml.
|
|
4
4
|
date: 2026-06-26
|
|
5
5
|
excerpt: One reserved frontmatter field on the home page — site_url — turns on the whole feeds story. No config loader, no plugin.
|
|
6
|
+
tags: [feeds, seo]
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# Shipping feeds without a config file
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Typed frontmatter with a four-line schema
|
|
3
|
+
description: Drop a _schema.wd in a collection and every entry is validated at build time — missing fields, wrong types, and typos all fail the build.
|
|
4
|
+
date: 2026-06-29
|
|
5
|
+
excerpt: A typo'd frontmatter key used to ship silently. With a collection schema, titel instead of title fails the build with a file and line.
|
|
6
|
+
tags: [collections, schema, dx]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Typed frontmatter with a four-line schema
|
|
10
|
+
|
|
11
|
+
Frontmatter is convenient until a typo bites. You write `titel:` instead of
|
|
12
|
+
`title:`, the field silently becomes empty, and the bug surfaces as a blank
|
|
13
|
+
heading in production. Darkmown closes that gap with an *optional* schema — one
|
|
14
|
+
file at the collection root, frontmatter-shaped, written exactly like the
|
|
15
|
+
frontmatter it checks:
|
|
16
|
+
|
|
17
|
+
```wd
|
|
18
|
+
---
|
|
19
|
+
title: string
|
|
20
|
+
date: date
|
|
21
|
+
description: string
|
|
22
|
+
excerpt: string?
|
|
23
|
+
tags: string[]?
|
|
24
|
+
---
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The vocabulary is small and closed on purpose: `string`, `number`, `boolean`,
|
|
28
|
+
`date`, and `string[]`, with a trailing `?` to make a field optional. That is
|
|
29
|
+
the whole DSL. An unknown type token is a compile error in the schema file
|
|
30
|
+
itself.
|
|
31
|
+
|
|
32
|
+
## What it catches
|
|
33
|
+
|
|
34
|
+
At build time, every entry in the collection is checked against the rules. A
|
|
35
|
+
missing required field, a value of the wrong type, or an extra field the schema
|
|
36
|
+
never declared each fails the build — and the error names the offending entry
|
|
37
|
+
by path and the field by name, so the fix is obvious:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
Missing required field "date" (date) in /blog/example/ (collection "blog").
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Validation is opt-in. No `_schema.wd`, no validation — the same posture as
|
|
44
|
+
`scoped` styles and the `draft` flag. Add the file when you want the guardrails;
|
|
45
|
+
leave it out while you are still sketching.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Zero JavaScript, by default
|
|
3
|
+
description: A Darkmown page ships no framework JavaScript unless it declares reactive behavior. The blog you are reading proves it.
|
|
4
|
+
date: 2026-06-25
|
|
5
|
+
excerpt: View source on this page. There is no runtime script tag, because nothing on it is reactive — and the framework knows the difference.
|
|
6
|
+
tags: [performance, philosophy]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Zero JavaScript, by default
|
|
10
|
+
|
|
11
|
+
Open your browser's view-source on this post. Look for a `<script>` that loads a
|
|
12
|
+
framework runtime. There isn't one — and there isn't one on the blog index, or
|
|
13
|
+
on any of its paginated pages either. None of them declare reactive behavior, so
|
|
14
|
+
none of them pay for it.
|
|
15
|
+
|
|
16
|
+
That is the whole bargain. A `.md` file is plain CommonMark. Rename it to `.wd`
|
|
17
|
+
and you unlock directives — state, loops, conditionals — but you only ship the
|
|
18
|
+
~7.4 KB runtime on the pages that actually use them. A listing built from a
|
|
19
|
+
collection loop resolves entirely at build time, so it stays static HTML.
|
|
20
|
+
|
|
21
|
+
## The extension is the feature gate
|
|
22
|
+
|
|
23
|
+
There is no flag to toggle, no "static export" mode to remember. The compiler
|
|
24
|
+
tracks whether a page declared anything reactive and emits `runtime: false` into
|
|
25
|
+
`routes.json` when it didn't. A collection listing, a paginated archive, a page
|
|
26
|
+
of prose with a code block — all static, all cacheable on a CDN forever, all
|
|
27
|
+
fast on the first byte because there is nothing to hydrate.
|
|
28
|
+
|
|
29
|
+
When you do want an island of interactivity — a search box, a cart, a sortable
|
|
30
|
+
table — you declare it, and only that page loads the runtime. The rest of the
|
|
31
|
+
site stays weightless.
|
package/site/pages/data.skin
CHANGED
|
@@ -29,18 +29,44 @@ h2
|
|
|
29
29
|
padding-top 1.2rem
|
|
30
30
|
|
|
31
31
|
button
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
display inline-flex
|
|
33
|
+
align-items center
|
|
34
|
+
justify-content center
|
|
35
|
+
gap .5rem
|
|
36
|
+
border 1px solid $accent
|
|
37
|
+
bg $accent
|
|
34
38
|
color white
|
|
35
|
-
padding .
|
|
39
|
+
padding .6rem 1.1rem
|
|
36
40
|
margin .3rem .5rem .3rem 0
|
|
37
|
-
radius
|
|
38
|
-
font
|
|
41
|
+
radius 999px
|
|
42
|
+
font system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
|
|
43
|
+
font-size .92rem
|
|
44
|
+
font-weight 560
|
|
45
|
+
line-height 1.15
|
|
39
46
|
cursor pointer
|
|
47
|
+
appearance none
|
|
48
|
+
box-shadow 0 1px 2px rgba(23,23,23,.08)
|
|
49
|
+
transition transform .14s cubic-bezier(.2,.7,.3,1), box-shadow .14s ease, background .14s ease, border-color .14s ease
|
|
40
50
|
|
|
41
51
|
button:hover
|
|
42
52
|
bg $accent
|
|
43
53
|
border-color $accent
|
|
54
|
+
transform translateY(-1px)
|
|
55
|
+
box-shadow 0 8px 18px -8px rgba(22,100,90,.5)
|
|
56
|
+
|
|
57
|
+
button:active
|
|
58
|
+
transform translateY(0)
|
|
59
|
+
box-shadow 0 1px 2px rgba(23,23,23,.08)
|
|
60
|
+
|
|
61
|
+
button:focus-visible
|
|
62
|
+
outline 2px solid $accent
|
|
63
|
+
outline-offset 2px
|
|
64
|
+
|
|
65
|
+
button:disabled
|
|
66
|
+
opacity .5
|
|
67
|
+
cursor not-allowed
|
|
68
|
+
box-shadow none
|
|
69
|
+
transform none
|
|
44
70
|
|
|
45
71
|
a
|
|
46
72
|
color $accent
|
|
@@ -75,21 +101,36 @@ form
|
|
|
75
101
|
margin 1rem 0
|
|
76
102
|
|
|
77
103
|
input
|
|
78
|
-
padding .
|
|
104
|
+
padding .62rem .85rem
|
|
79
105
|
border 1px solid $line
|
|
80
106
|
radius $radius
|
|
81
107
|
font inherit
|
|
108
|
+
font-size .95rem
|
|
82
109
|
bg $panel
|
|
110
|
+
appearance none
|
|
111
|
+
transition border-color .14s ease, box-shadow .14s ease
|
|
112
|
+
|
|
113
|
+
input:hover
|
|
114
|
+
border-color $accent
|
|
115
|
+
|
|
116
|
+
input:focus-visible
|
|
117
|
+
outline none
|
|
118
|
+
border-color $accent
|
|
119
|
+
box-shadow 0 0 0 3px rgba(22,100,90,.18)
|
|
83
120
|
|
|
84
121
|
.pulse-button
|
|
85
|
-
border
|
|
122
|
+
border 1px solid $warm
|
|
86
123
|
bg $warm
|
|
87
124
|
color white
|
|
88
|
-
padding .
|
|
89
|
-
radius
|
|
125
|
+
padding .6rem 1.1rem
|
|
126
|
+
radius 999px
|
|
90
127
|
font inherit
|
|
91
128
|
cursor pointer
|
|
92
129
|
|
|
130
|
+
.pulse-button:hover
|
|
131
|
+
transform translateY(-1px)
|
|
132
|
+
box-shadow 0 8px 18px -8px rgba(168,63,42,.5)
|
|
133
|
+
|
|
93
134
|
.tally-card
|
|
94
135
|
border 1px solid $line
|
|
95
136
|
bg $panel
|
package/site/pages/docs/index.wd
CHANGED
|
@@ -148,6 +148,53 @@ Nothing left to do.
|
|
|
148
148
|
|
|
149
149
|
These clauses stay build-time when the source and every argument are static — a sorted, limited loop over a JSON file ships zero JS. The loop turns reactive only when the source or a clause reads `:state`/`:store`/`:fetch` data.
|
|
150
150
|
|
|
151
|
+
## Content collections
|
|
152
|
+
|
|
153
|
+
Any folder under `site/pages/` is a **collection** — loop it by its bare name. No `content/` root, no marker file: a `site/pages/blog/` folder of posts *is* the `blog` collection. The whole [blog on this site](/blog/) is one such loop.
|
|
154
|
+
|
|
155
|
+
```wd
|
|
156
|
+
@loop blog into post sort by post.date desc
|
|
157
|
+
- [{ post.title }]({ post.url }) — { post.date }
|
|
158
|
+
@endloop
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Each entry's frontmatter is a row, plus three derived fields: `{ post.url }` (the route), `{ post.slug }` (the filename), and `{ post.excerpt }` (the frontmatter `excerpt:`, else the first paragraph of a `.md` body). It's the same loop with the same clauses, resolved at build time — a pure listing ships zero JS. Drafts never appear in a default-build listing (only under `darkmown build --drafts`).
|
|
162
|
+
|
|
163
|
+
### Typed schema — `_schema.wd`
|
|
164
|
+
|
|
165
|
+
Drop a `_schema.wd` at a collection's root to validate every entry at build time. It's frontmatter-shaped, one `field: type` per line:
|
|
166
|
+
|
|
167
|
+
```wd
|
|
168
|
+
---
|
|
169
|
+
title: string
|
|
170
|
+
date: date
|
|
171
|
+
excerpt: string?
|
|
172
|
+
tags: string[]?
|
|
173
|
+
---
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Closed types: `string`, `number`, `boolean`, `date`, `string[]`, each with `?` for optional. A missing required field, a wrong type, an unknown extra field (typo guard), or an unknown type token fails the build with a `file:line`. No `_schema.wd` means no validation — it's opt-in.
|
|
177
|
+
|
|
178
|
+
### Pagination — `paginate N`
|
|
179
|
+
|
|
180
|
+
Add `paginate N` (collections only) to split a listing into static pages. Page 1 keeps the listing route; pages 2+ are at `/<route>/page/2/`, `/page/3/`, … all static HTML.
|
|
181
|
+
|
|
182
|
+
```wd
|
|
183
|
+
@loop blog into post sort by post.date desc paginate 5
|
|
184
|
+
- [{ post.title }]({ post.url })
|
|
185
|
+
@endloop
|
|
186
|
+
|
|
187
|
+
Page { page.current } of { page.total }
|
|
188
|
+
:if page.prev
|
|
189
|
+
[← Newer]({ page.prev })
|
|
190
|
+
:endif
|
|
191
|
+
:if page.next
|
|
192
|
+
[Older →]({ page.next })
|
|
193
|
+
:endif
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The pager — `{ page.current }`, `{ page.total }`, `{ page.prev }`, `{ page.next }` (URLs, empty at the ends) — is exposed to the whole page as plain links. Zero JS. `paginate` can't combine with `offset`/`limit` (it owns the slice).
|
|
197
|
+
|
|
151
198
|
## Interpolation
|
|
152
199
|
|
|
153
200
|
One syntax everywhere: `{ name }` or `{ name.path }`.
|
package/site/pages/loops.skin
CHANGED
|
@@ -32,28 +32,65 @@ a
|
|
|
32
32
|
color $accent
|
|
33
33
|
|
|
34
34
|
button
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
display inline-flex
|
|
36
|
+
align-items center
|
|
37
|
+
justify-content center
|
|
38
|
+
gap .5rem
|
|
39
|
+
border 1px solid $accent
|
|
40
|
+
bg $accent
|
|
37
41
|
color white
|
|
38
|
-
padding .
|
|
42
|
+
padding .6rem 1.1rem
|
|
39
43
|
margin .3rem .5rem .3rem 0
|
|
40
|
-
radius
|
|
41
|
-
font
|
|
44
|
+
radius 999px
|
|
45
|
+
font system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
|
|
46
|
+
font-size .92rem
|
|
47
|
+
font-weight 560
|
|
48
|
+
line-height 1.15
|
|
42
49
|
cursor pointer
|
|
50
|
+
appearance none
|
|
51
|
+
box-shadow 0 1px 2px rgba(23,23,23,.08)
|
|
52
|
+
transition transform .14s cubic-bezier(.2,.7,.3,1), box-shadow .14s ease, background .14s ease, border-color .14s ease
|
|
43
53
|
|
|
44
54
|
button:hover
|
|
45
55
|
bg $accent
|
|
46
56
|
border-color $accent
|
|
57
|
+
transform translateY(-1px)
|
|
58
|
+
box-shadow 0 8px 18px -8px rgba(22,100,90,.5)
|
|
59
|
+
|
|
60
|
+
button:active
|
|
61
|
+
transform translateY(0)
|
|
62
|
+
box-shadow 0 1px 2px rgba(23,23,23,.08)
|
|
63
|
+
|
|
64
|
+
button:focus-visible
|
|
65
|
+
outline 2px solid $accent
|
|
66
|
+
outline-offset 2px
|
|
67
|
+
|
|
68
|
+
button:disabled
|
|
69
|
+
opacity .5
|
|
70
|
+
cursor not-allowed
|
|
71
|
+
box-shadow none
|
|
72
|
+
transform none
|
|
47
73
|
|
|
48
74
|
input
|
|
49
|
-
padding .
|
|
75
|
+
padding .62rem .85rem
|
|
50
76
|
border 1px solid $line
|
|
51
77
|
radius $radius
|
|
52
78
|
font inherit
|
|
79
|
+
font-size .95rem
|
|
53
80
|
bg $panel
|
|
54
81
|
width 100%
|
|
55
82
|
max-width 360px
|
|
56
83
|
margin 1rem 0
|
|
84
|
+
appearance none
|
|
85
|
+
transition border-color .14s ease, box-shadow .14s ease
|
|
86
|
+
|
|
87
|
+
input:hover
|
|
88
|
+
border-color $accent
|
|
89
|
+
|
|
90
|
+
input:focus-visible
|
|
91
|
+
outline none
|
|
92
|
+
border-color $accent
|
|
93
|
+
box-shadow 0 0 0 3px rgba(22,100,90,.18)
|
|
57
94
|
|
|
58
95
|
ul
|
|
59
96
|
border 1px solid $line
|
package/site/pages/reactive.skin
CHANGED
|
@@ -29,18 +29,44 @@ h2
|
|
|
29
29
|
padding-top 1.2rem
|
|
30
30
|
|
|
31
31
|
button
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
display inline-flex
|
|
33
|
+
align-items center
|
|
34
|
+
justify-content center
|
|
35
|
+
gap .5rem
|
|
36
|
+
border 1px solid $accent
|
|
37
|
+
bg $accent
|
|
34
38
|
color white
|
|
35
|
-
padding .
|
|
39
|
+
padding .6rem 1.1rem
|
|
36
40
|
margin .3rem .5rem .3rem 0
|
|
37
|
-
radius
|
|
38
|
-
font
|
|
41
|
+
radius 999px
|
|
42
|
+
font system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
|
|
43
|
+
font-size .92rem
|
|
44
|
+
font-weight 560
|
|
45
|
+
line-height 1.15
|
|
39
46
|
cursor pointer
|
|
47
|
+
appearance none
|
|
48
|
+
box-shadow 0 1px 2px rgba(23,23,23,.08)
|
|
49
|
+
transition transform .14s cubic-bezier(.2,.7,.3,1), box-shadow .14s ease, background .14s ease, border-color .14s ease
|
|
40
50
|
|
|
41
51
|
button:hover
|
|
42
52
|
bg $accent
|
|
43
53
|
border-color $accent
|
|
54
|
+
transform translateY(-1px)
|
|
55
|
+
box-shadow 0 8px 18px -8px rgba(22,100,90,.5)
|
|
56
|
+
|
|
57
|
+
button:active
|
|
58
|
+
transform translateY(0)
|
|
59
|
+
box-shadow 0 1px 2px rgba(23,23,23,.08)
|
|
60
|
+
|
|
61
|
+
button:focus-visible
|
|
62
|
+
outline 2px solid $accent
|
|
63
|
+
outline-offset 2px
|
|
64
|
+
|
|
65
|
+
button:disabled
|
|
66
|
+
opacity .5
|
|
67
|
+
cursor not-allowed
|
|
68
|
+
box-shadow none
|
|
69
|
+
transform none
|
|
44
70
|
|
|
45
71
|
a
|
|
46
72
|
color $accent
|
|
@@ -89,4 +115,10 @@ ul
|
|
|
89
115
|
bg transparent
|
|
90
116
|
color $warm
|
|
91
117
|
border 1px solid $line
|
|
92
|
-
padding .
|
|
118
|
+
padding .4rem .9rem
|
|
119
|
+
box-shadow none
|
|
120
|
+
|
|
121
|
+
.card.line button:hover
|
|
122
|
+
bg $warm
|
|
123
|
+
border-color $warm
|
|
124
|
+
color white
|
package/site/pages/store.skin
CHANGED
|
@@ -32,28 +32,65 @@ a
|
|
|
32
32
|
color $accent
|
|
33
33
|
|
|
34
34
|
button
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
display inline-flex
|
|
36
|
+
align-items center
|
|
37
|
+
justify-content center
|
|
38
|
+
gap .5rem
|
|
39
|
+
border 1px solid $accent
|
|
40
|
+
bg $accent
|
|
37
41
|
color white
|
|
38
|
-
padding .
|
|
42
|
+
padding .6rem 1.1rem
|
|
39
43
|
margin .3rem .5rem .3rem 0
|
|
40
|
-
radius
|
|
41
|
-
font
|
|
44
|
+
radius 999px
|
|
45
|
+
font system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
|
|
46
|
+
font-size .92rem
|
|
47
|
+
font-weight 560
|
|
48
|
+
line-height 1.15
|
|
42
49
|
cursor pointer
|
|
50
|
+
appearance none
|
|
51
|
+
box-shadow 0 1px 2px rgba(23,23,23,.08)
|
|
52
|
+
transition transform .14s cubic-bezier(.2,.7,.3,1), box-shadow .14s ease, background .14s ease, border-color .14s ease
|
|
43
53
|
|
|
44
54
|
button:hover
|
|
45
55
|
bg $accent
|
|
46
56
|
border-color $accent
|
|
57
|
+
transform translateY(-1px)
|
|
58
|
+
box-shadow 0 8px 18px -8px rgba(22,100,90,.5)
|
|
59
|
+
|
|
60
|
+
button:active
|
|
61
|
+
transform translateY(0)
|
|
62
|
+
box-shadow 0 1px 2px rgba(23,23,23,.08)
|
|
63
|
+
|
|
64
|
+
button:focus-visible
|
|
65
|
+
outline 2px solid $accent
|
|
66
|
+
outline-offset 2px
|
|
67
|
+
|
|
68
|
+
button:disabled
|
|
69
|
+
opacity .5
|
|
70
|
+
cursor not-allowed
|
|
71
|
+
box-shadow none
|
|
72
|
+
transform none
|
|
47
73
|
|
|
48
74
|
input
|
|
49
|
-
padding .
|
|
75
|
+
padding .62rem .85rem
|
|
50
76
|
border 1px solid $line
|
|
51
77
|
radius $radius
|
|
52
78
|
font inherit
|
|
79
|
+
font-size .95rem
|
|
53
80
|
bg $panel
|
|
54
81
|
width 100%
|
|
55
82
|
max-width 360px
|
|
56
83
|
margin 1rem 0
|
|
84
|
+
appearance none
|
|
85
|
+
transition border-color .14s ease, box-shadow .14s ease
|
|
86
|
+
|
|
87
|
+
input:hover
|
|
88
|
+
border-color $accent
|
|
89
|
+
|
|
90
|
+
input:focus-visible
|
|
91
|
+
outline none
|
|
92
|
+
border-color $accent
|
|
93
|
+
box-shadow 0 0 0 3px rgba(22,100,90,.18)
|
|
57
94
|
|
|
58
95
|
#shop
|
|
59
96
|
border 1px solid $line
|