@agentic-design-kits/verdigris 0.0.0-stage → 2026.1113.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/COMPONENTS.md +997 -0
- package/DESIGN.md +225 -0
- package/LICENSE +21 -0
- package/README.md +73 -2
- package/fonts/Cormorant-LICENSE.txt +93 -0
- package/fonts/Cormorant-Light.woff2 +0 -0
- package/fonts/Cormorant-LightItalic.woff2 +0 -0
- package/fonts/Cormorant-Medium.woff2 +0 -0
- package/fonts/Cormorant-MediumItalic.woff2 +0 -0
- package/fonts/Cormorant-SemiBold.woff2 +0 -0
- package/fonts/Cormorant-SemiBoldItalic.woff2 +0 -0
- package/kit/astro/BaseLayout.astro +31 -0
- package/kit/astro/Mark.astro +25 -0
- package/kit/astro/Rosette.astro +31 -0
- package/kit/astro/font.ts +2 -0
- package/kit/example.html +186 -0
- package/kit/fonts.css +50 -0
- package/kit/shared/astro/BottomBar.ts +2 -0
- package/kit/shared/astro/CardGrid.ts +2 -0
- package/kit/shared/astro/Cta.ts +2 -0
- package/kit/shared/astro/Dialog.ts +2 -0
- package/kit/shared/astro/Drawer.ts +2 -0
- package/kit/shared/astro/DrawerButton.ts +2 -0
- package/kit/shared/astro/Ellipse.ts +2 -0
- package/kit/shared/astro/FavoriteButton.ts +2 -0
- package/kit/shared/astro/FeaturesList.ts +2 -0
- package/kit/shared/astro/FilePicker.ts +2 -0
- package/kit/shared/astro/Footer.ts +2 -0
- package/kit/shared/astro/Hero.ts +2 -0
- package/kit/shared/astro/IconTemplates.ts +2 -0
- package/kit/shared/astro/MenuButton.ts +2 -0
- package/kit/shared/astro/MenuItems.ts +2 -0
- package/kit/shared/astro/MenuPanel.ts +2 -0
- package/kit/shared/astro/NumCircle.ts +2 -0
- package/kit/shared/astro/OutputPanel.ts +2 -0
- package/kit/shared/astro/PageTitle.ts +2 -0
- package/kit/shared/astro/Pager.ts +2 -0
- package/kit/shared/astro/PostFeature.ts +2 -0
- package/kit/shared/astro/PostHeader.ts +2 -0
- package/kit/shared/astro/PostList.ts +2 -0
- package/kit/shared/astro/PostNav.ts +2 -0
- package/kit/shared/astro/SummaryList.ts +2 -0
- package/kit/shared/astro/ThemeToggle.ts +2 -0
- package/kit/shared/dialog.ts +2 -0
- package/kit/shared/drawer.ts +2 -0
- package/kit/shared/favorites.ts +2 -0
- package/kit/shared/file-pick.ts +2 -0
- package/kit/shared/icon-controls.ts +2 -0
- package/kit/shared/jump.ts +2 -0
- package/kit/shared/marquee.ts +2 -0
- package/kit/shared/new-dot.ts +2 -0
- package/kit/shared/output.ts +2 -0
- package/kit/shared/panel.ts +2 -0
- package/kit/tailwind.css +24 -0
- package/kit/types.ts +3 -0
- package/kit/verdigris.css +177 -0
- package/package.json +62 -4
- package/tokens.json +618 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Verdigris
|
|
2
|
+
|
|
3
|
+
A book-warm look built on Swiss Minimal's grid: one family (Cormorant), a cream page, green-black ink, verdigris for what matters and copper for ornament. The structure stays Swiss; ornament is allowed where it earns its place. It works for a journal, a portfolio, a nonprofit or a product site; nothing in it assumes a subject.
|
|
4
|
+
|
|
5
|
+
## What to load
|
|
6
|
+
|
|
7
|
+
Verdigris is a package, built for **Astro with Tailwind v4** (no React needed). Install it (`npm install @agentic-design-kits/verdigris`) and import from `@agentic-design-kits/verdigris`: `@agentic-design-kits/verdigris/tailwind.css`, `@agentic-design-kits/verdigris/astro/Hero.astro`, `@agentic-design-kits/verdigris/output.js`, `@agentic-design-kits/verdigris/types`. It is built on Swiss Minimal, which is installed with it. The paths below say where each file lives: `kit/` and `fonts/` are this package, and the ones marked Swiss Minimal are in `node_modules/@agentic-design-kits/swiss-minimal/kit/`.
|
|
8
|
+
|
|
9
|
+
- `kit/tailwind.css`: the entry point for a Tailwind site. Imports Swiss Minimal's `tailwind.css` (Tailwind itself, `theme.css` in the `components` layer, and the token utilities), then `fonts.css` and `verdigris.css` in the same layer. See "With Tailwind v4".
|
|
10
|
+
- `theme.css` (Swiss Minimal): every token (light and dark), the base styles and every component class, in plain CSS. It brings Swiss Minimal's own typeface; `kit/fonts.css` here sets `--font-sans` to Cormorant after it, and `kit/astro/font.ts` names the font Verdigris's `BaseLayout` preloads.
|
|
11
|
+
- `kit/verdigris.css`: everything that makes this Verdigris: token overrides, type features and the new parts. `tailwind.css` loads it after `theme.css`; a plain page links it after `theme.css`. It also works on its own, without Tailwind. Dark mode follows the system unless `<html data-theme="light|dark">` is set.
|
|
12
|
+
- `panel.js` (Swiss Minimal, imported as `@agentic-design-kits/verdigris/panel.js`): the slide-down panel's behavior, tabs included. Plain JavaScript, no dependencies, like the other scripts: `new-dot.js` (the "new" dot), `drawer.js` (the second, non-modal panel), `jump.js` (jump to the current item), `marquee.js` (a scrolling title), `file-pick.js` (the file picker), `output.js` (live output), `dialog.js` (fills in for the native dialog where a browser is behind), `favorites.js` (favorite stars) and `icon-controls.js` (icon buttons for rows built in the browser).
|
|
13
|
+
- The Astro components, imported as `@agentic-design-kits/verdigris/astro/Name.astro`. `BaseLayout`, `Mark` and `Rosette` are written here (`kit/astro/`); the rest are Swiss Minimal's, passed on by `kit/shared/`: BaseLayout, Hero, PageTitle, Mark, Rosette, PostList, PostFeature, Pager, PostHeader, PostNav, Footer, BottomBar, FilePicker, SummaryList, OutputPanel, Dialog, Ellipse, NumCircle, MenuButton, MenuPanel, MenuItems, ThemeToggle, Drawer, DrawerButton, FavoriteButton, FeaturesList, IconTemplates (needs `astro-icon`) and the types (`MenuItem`, `PanelTab`, `NewDot`, `Feature`, `Post`, `PostLink`). They use Tailwind utilities for layout and the kit classes for components.
|
|
14
|
+
- `kit/example.html`: a one-page demo using only `theme.css` and `verdigris.css`. Open it from the repository (it reads Swiss Minimal's `theme.css` from the `kit/` at the root) to see what "correct" looks like.
|
|
15
|
+
- `fonts/Cormorant-*.woff2` (six files) and `fonts/Cormorant-LICENSE.txt`, SIL OFL 1.1. Keep the license beside the fonts. Source Code Pro, for code, comes with Swiss Minimal.
|
|
16
|
+
- `tokens.json`: the token values as data. If a value in `theme.css` ever differs, `tokens.json` wins.
|
|
17
|
+
|
|
18
|
+
Put this in `<head>` before first paint so a saved theme does not flash:
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<script>
|
|
22
|
+
try { var t = localStorage.getItem('theme'); if (t === 'light' || t === 'dark') document.documentElement.dataset.theme = t; } catch (e) {}
|
|
23
|
+
</script>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Principles
|
|
27
|
+
|
|
28
|
+
- **Structure is Swiss.** One grid, flat color, square corners, and rules that do real work. Everything else on this page sits on top of that.
|
|
29
|
+
- **One family, many voices.** Cormorant in light (display), medium (text), semibold (names and labels) and italic (asides). The one exception is code, set in Source Code Pro.
|
|
30
|
+
- **Ornament earns its place.** Decoration is allowed, within limits: it comes from the font first, it never carries meaning alone, and a page uses each large ornament once. See Ornament.
|
|
31
|
+
- **Scotch rule, thin rule.** A scotch rule (thick and thin, read as one) starts each section. Rows are separated by a 1px `rule`, with none after the last one.
|
|
32
|
+
- **One grid.** Section headings, list rows and descriptions share the same columns.
|
|
33
|
+
- **Two metals.** `accent` (verdigris) marks the one thing that matters on a screen. Copper is for labels and ornament, and never marks an action.
|
|
34
|
+
- **No capitals.** No all-caps text and no small caps, anywhere. See Content and voice.
|
|
35
|
+
- **Flat.** No shadows, gradients or glass in the interface. The kit's six round marks stay, and the rosette is line art, not a surface.
|
|
36
|
+
|
|
37
|
+
## Content and voice
|
|
38
|
+
|
|
39
|
+
- Plain, short, declarative. "Send", "Menu", "All items". No exclamation marks, no emoji.
|
|
40
|
+
- **No all caps, anywhere, for accessibility.** Capitals have no word shapes, so many readers go letter by letter, and some screen readers spell a capitalized word out. That covers `text-transform: uppercase`, small caps (they have the same even outline) and capitals typed into a label. Labels, buttons and tabs are sentence case, kept to one or two words, and stand out by weight, place and copper. Real acronyms in running text, like "AI", are fine.
|
|
41
|
+
- An oval badge holds a short lowercase word, never a country code.
|
|
42
|
+
- A filtered view always says what it shows: "All items", "Writing · 7 items".
|
|
43
|
+
- Write the site's name in `display`, weight 300. The name set in type is the mark. A site may put one small symbol before it in the top bar (the demo's is Cormorant's fleuron), in the accent. Never a logo image.
|
|
44
|
+
|
|
45
|
+
## Color
|
|
46
|
+
|
|
47
|
+
Light is the default. Dark keeps the same roles.
|
|
48
|
+
|
|
49
|
+
- Page: `paper` ground, `ink` text and rules, `muted` for secondary text, `rule` for thin dividers.
|
|
50
|
+
- **Verdigris has three tokens, by size and ground.** `accent` is for large shapes and the focus ring only. Small text on `paper` uses `accent-text`; on a dark surface, `accent-bright`. Never set small text in plain `accent` (3.4:1 on light `paper`).
|
|
51
|
+
- **Copper has three too:** `copper` for large ornament, `copper-text` for labels and small ornament on `paper`, `copper-bright` on `bar`. Copper never marks an action, an active state or a link.
|
|
52
|
+
- The primary button is `accent-text` with `accent-ink` text.
|
|
53
|
+
- **Dark surfaces stay dark in both themes.** The panel, the drawer, the top bar while either is open, a bottom bar and the toast are `bar` with `bar-ink` text, a green-black in both themes.
|
|
54
|
+
- Small labels on `bar` are semibold `accent-bright` or `copper-bright`, never faded white.
|
|
55
|
+
- List rows fill on hover and when active: `row-hover-bg` / `row-active-bg` with every piece of text in `row-fg`. The active row also gets a 6px `row-edge` on its left.
|
|
56
|
+
- **Code colors are the exception.** Highlighted code keeps magenta, teal and orange, only inside `pre` and `code`, each passing 4.5:1 in both themes. See CodeBlock in `COMPONENTS.md`.
|
|
57
|
+
- Don't add a color. A new state reuses these roles. `data-accent` changes nothing here: Verdigris has one palette.
|
|
58
|
+
|
|
59
|
+
## Type
|
|
60
|
+
|
|
61
|
+
One family, `--font-sans` (Cormorant, falling back to Garamond, Iowan Old Style, Georgia). Code is the only exception: `code`, `pre`, `kbd` and `samp` use `--font-mono`, Source Code Pro at 400, scaled to 0.8em because it is far larger on its body than Cormorant.
|
|
62
|
+
|
|
63
|
+
| Style | Use |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `display` | Hero title, `clamp(4.75rem, 12.4vw, 11rem)`, 300, line height 0.88, -0.02em |
|
|
66
|
+
| `h2` | Section title, 2.75rem then 3.75rem from 768px, 300, -0.025em |
|
|
67
|
+
| `title` | A list item's name, 1.625rem then 1.875rem, 600, +0.01em |
|
|
68
|
+
| `lead` | Intro paragraph, 1.75rem then 1.875rem from 768px, 300 italic |
|
|
69
|
+
| `body` | Body copy, 1.375rem then 1.625rem from 768px, 500, line height 1.55, Garamond forms on (`ss02`) |
|
|
70
|
+
| `small` | Meta, sublines, counts, 1.125rem |
|
|
71
|
+
| `label` | 1 to 2 words, sentence case, 600 |
|
|
72
|
+
| `code` | Code, file names, commands, keys: `--font-mono` at 0.8em, never under 0.875rem |
|
|
73
|
+
|
|
74
|
+
- **18px is the floor.** Cormorant's x-height is 0.386 of its size against Overused Grotesk's 0.5, so 18px reads about as tall as 14px there. Code is the exception, as its x-height is large.
|
|
75
|
+
- **Weight follows size.** Light at display sizes, medium for reading, semibold for names. Never light under 1.75rem: the hairlines disappear.
|
|
76
|
+
- Figures are old-style in text. Add `.num` (lining, tabular) to any number that lines up in a column.
|
|
77
|
+
- Ligatures (`dlig`, `hlig`) and calligraphic forms (`ss01`) are for display sizes only.
|
|
78
|
+
- Every size, weight, body line height and label style is a token at the top of `theme.css`, set for Verdigris in `verdigris.css`. A new rule uses these tokens, never a raw value.
|
|
79
|
+
- Code is plain HTML, no class, as in Swiss Minimal; see CodeBlock in `COMPONENTS.md`. Monospace never goes beyond code.
|
|
80
|
+
|
|
81
|
+
## Layout
|
|
82
|
+
|
|
83
|
+
- Content width `page-max` (88rem), centered in `.page`, with `gutter` side padding (1rem, 2rem from 640px).
|
|
84
|
+
- A scotch rule opens each section (`.rule-thick`).
|
|
85
|
+
- Hero with generous space (`space-12`, `space-20` from 768px), then sections separated by `space-16`. A hero image takes its layout from its shape: square or portrait sits beside the text, 50/50 from 768px; landscape goes under the text at the full page width. Phones always stack, text first.
|
|
86
|
+
- Section header: scotch rule, then the grid row (range label, `h2` title, blurb).
|
|
87
|
+
- List rows: 1px `rule` bottom border, none on the last row, `space-4` vertical padding. The row number sits `space-4` in from the left (1.25rem from 768px) so it stays clear of the active row's 6px edge.
|
|
88
|
+
- Mobile first. Rows stack on phones and become a column grid from 768px.
|
|
89
|
+
- Pages end with a `Footer`: the scotch rule, the site name and its links.
|
|
90
|
+
- A fixed bottom bar (`BottomBar`) is optional, and goes only on pages where something stays current while the page scrolls. On those pages, set `bar-h` to its height and reserve that space at the bottom of the page (`BaseLayout`'s `barHeight` prop does both). Without one, `--bar-h` is `0rem`.
|
|
91
|
+
- A page that needs a name but no hero opens with a `PageTitle`: one big `h1` across the width of `.page`, on the paper or on a `bar` block.
|
|
92
|
+
- A blog's index may open with a `PostFeature` (one post beside a square image) in place of a hero, kept at the top of every page when a `Pager` splits the list. A blog post opens with a `PostHeader`, its text in a `.post-body` at a 42rem measure, and ends with a `PostNav`.
|
|
93
|
+
- Everything is square: `radius-none`. `radius-round` is for six marks only: the spinner, the "new" dot (`.new-dot`), the drawer's "more below" arrow (`.drawer-more`), the radio button (`.radio`), the oval badge (`.ellipse`, at 50%) and the number circle (`.num-circle`, at 50%). The switch stays square.
|
|
94
|
+
|
|
95
|
+
## Iconography
|
|
96
|
+
|
|
97
|
+
- **Glyphs come first.** Use the font's own: ❦ ⁂ † ‡ § ☞ ◆ and the arrows. CSS draws the menu bars and the plus and minus.
|
|
98
|
+
- **Marks are the icon set.** `Mark.astro` holds twelve drawn icons (menu, search, next, open, close, journal, write, letter, save, leaf, date, place): 24px grid, 1.25px line, round ends. Add new ones in the same pen.
|
|
99
|
+
- **A mark may stand beside its label,** which Swiss Minimal did not allow. It should add recognition, not repeat a whole row of the same picture.
|
|
100
|
+
- Third-party brand marks (YouTube, Spotify) still use Simple Icons, and a few actions Material Symbols, through `astro-icon`.
|
|
101
|
+
- **One color, always:** `currentColor`, turning `accent-bright` (on `bar`) or `accent-text` (on `paper`) on hover. Never a brand's own colors.
|
|
102
|
+
- **Sized to the text:** 1.625rem beside body text. Hit area at least 44px square.
|
|
103
|
+
- **Every icon-only control** gets a verb-first `aria-label` and a `title` tooltip. The icon itself is `aria-hidden="true"`.
|
|
104
|
+
- No icon font, no icon CDN, and no trash can: remove is a circled X.
|
|
105
|
+
|
|
106
|
+
## Ornament
|
|
107
|
+
|
|
108
|
+
Ornament is what separates this kit from Swiss Minimal, so it has its own limits.
|
|
109
|
+
|
|
110
|
+
- **Fleuron rule** (`.rule-fleuron`): a pause between parts of a page. Once a page at most.
|
|
111
|
+
- **Asterism** (⁂): the end of a piece, or a Markdown `---` in a post.
|
|
112
|
+
- **Dagger** (†): a footnote mark.
|
|
113
|
+
- **Manicule** (☞): a `.notice`, for one note a reader must not miss.
|
|
114
|
+
- **Lozenge** (◆): separates metadata.
|
|
115
|
+
- **Drop cap** (`.post-body--cap`): a post's first letter, through `::first-letter`, so screen readers still get the whole word.
|
|
116
|
+
- **Plate** (`.plate`): a framed quotation or title. One to a page, never around an image or a control.
|
|
117
|
+
- **Rosette** (`Rosette.astro`): engraved line work for a page with no picture of its own. It stays a figure on the grid: no text on it, and never a background.
|
|
118
|
+
- Ornament is `aria-hidden="true"` and never the only way to say something. It is copper or verdigris, never both in one mark, the rosette aside.
|
|
119
|
+
|
|
120
|
+
## Images
|
|
121
|
+
|
|
122
|
+
Photos, artwork, screenshots and product shots are welcome. The test is whether the image is content: it shows the work, the person, the place or the thing the page is about. Filler is not: stock mood shots, background textures, patterns behind text.
|
|
123
|
+
|
|
124
|
+
- **Plain rectangles.** Square corners, no border, frame, shadow, overlay, tint or filter. The image's own edge is the edge.
|
|
125
|
+
- **On the grid.** An image spans whole columns and lines up with the text edges beside it. Inside the page it is never wider than `page-max`; a full-bleed image runs edge to edge with no gutter.
|
|
126
|
+
- **Fixed shapes.** Use one of 1:1, 4:3, 3:2 or 16:9 and crop to fill (`.media` with `--ratio`). Images in one list share a ratio.
|
|
127
|
+
- **Color stays.** An image keeps its own colors; they do not count as adding a color to the palette. Do not recolor or desaturate to match.
|
|
128
|
+
- **No text on images.** Titles and captions go beside or below. If text must sit on an image, put it on a solid `bar` block, never straight on the picture.
|
|
129
|
+
- **Captions** go below in `small muted`, left aligned, sentence case. Credits use the same style.
|
|
130
|
+
- **Dark mode.** Do not dim or invert images. A light image on the dark page reads as a light block, which is fine; if the art has a dark version, swap it with the theme.
|
|
131
|
+
- **Loading.** Give every image its width and height (or a ratio) so the page does not jump. The empty box fills with `rule` until it loads. Lazy-load everything except the hero.
|
|
132
|
+
- **Alt text** says what the image shows in one sentence. An image that only repeats the text beside it gets `alt=""`.
|
|
133
|
+
- **No motion.** No parallax, zoom-on-scroll, slow pan or auto-advancing carousel. A hover may swap the image; it never scales it.
|
|
134
|
+
|
|
135
|
+
## Motion
|
|
136
|
+
|
|
137
|
+
- Short and purposeful: 150ms hover swaps, 400 to 550ms slides with `--ease-out` (`cubic-bezier(0.22, 1, 0.36, 1)`).
|
|
138
|
+
- Only the activity indicator, the spinner, the panel and drawer, a dialog, accordions and panel tabs, an overlong title, the jump flash and a turning rosette move. A title scrolls three rounds at most, then stops. A rosette turns once in two minutes, always beside a switch that stops it.
|
|
139
|
+
- Hover labels wait 350ms before they show, so passing the pointer over the top bar doesn't flicker them.
|
|
140
|
+
- Always honor `prefers-reduced-motion`; `theme.css` switches slides, fades and loops off.
|
|
141
|
+
|
|
142
|
+
## States and accessibility
|
|
143
|
+
|
|
144
|
+
- Focus ring: 3px solid `accent` outline, 2px offset, on everything focusable. Never hide it.
|
|
145
|
+
- Every page starts with a skip link and has one `<main>`, so a keyboard user can go past the top bar. `BaseLayout` writes both; see SkipLink in `COMPONENTS.md`.
|
|
146
|
+
- Hand cursor on everything clickable (`theme.css` sets it, since Tailwind v4 removes it from buttons).
|
|
147
|
+
- Hover keeps text readable: when a row or button fills, its text switches to the opposite color.
|
|
148
|
+
- Pressed filter buttons use `aria-pressed="true"`; the current page uses `aria-current="page"`.
|
|
149
|
+
- External links announce "(opens in a new tab)" to screen readers.
|
|
150
|
+
- A control's name says what it does, verb first: "Show Field notes in the list", "Remove: Field notes". A toggle keeps one name and uses `aria-pressed`.
|
|
151
|
+
- When a control is about to disappear with focus on it (a removed row, an arrow that hides once used), move focus to the next sensible place first.
|
|
152
|
+
- Remember things in localStorage only as a convenience: wrap every read and write, and make the page work the same without it.
|
|
153
|
+
- Text at 18px or more, contrast 4.5:1 or better in both themes.
|
|
154
|
+
- No capitals, as a rule of the kit: `tests/no-capitals.spec.ts` fails on uppercase or small caps in the built CSS.
|
|
155
|
+
|
|
156
|
+
## Components
|
|
157
|
+
|
|
158
|
+
Every component is a plain CSS class in `kit/theme.css` (loaded through `kit/tailwind.css`); there is no JavaScript bundle, only a few small plain scripts for the pieces that need behavior. In short: **Skip link** (`.skip-link`), **Top bar** (`.topbar`), **Outlined button** (`.topbar-btn`, `.burger`), **Hover label** (`data-tip`), **Primary button** (`.btn`), **Secondary button** (`.btn-outline`, `.btn-group`), **Tertiary button** (`.btn-outline--tertiary`), **Slide-down panel** (`.panel`, with `kit/panel.js`), **Menu** (`.menu-list`, `.acc`, `.submenu`), **Panel tabs** (`.panel-tabs`, `.panel-tab`), **"New" dot** (`.new-dot`, with `kit/new-dot.js`), **Field** (`.field`), **Select** (`.select-wrap`, `.select`), **Date and time** (`.field` on `type="date"`, `time`…), **File picker** (`.file-pick`, with `kit/file-pick.js`), **Switch** (`.choice`, `.switch`), **Radio** (`.choice-group`, `.radio`), **Filter buttons** (`.chip`), **Favorite star** (`.fav-btn`, with `kit/favorites.js`), **Page accordion** (`.page-acc`), **List row** (`.row`), **Bottom bar** (`.bottom-bar`, optional), **Jump to current** (`.jump-btn`, with `kit/jump.js`), **Scrolling title** (`.marquee`, with `kit/marquee.js`), **Dialog** (`.dialog` on `<dialog>`, with `kit/dialog.js`), **Toast** (`.toast`), **Drawer** (`.drawer`, with `kit/drawer.js`), **Scotch rule** (`.rule-scotch`, `.rule-thick`), **Fleuron rule** (`.rule-fleuron`), **Plate** (`.plate`), **Notice** (`.notice`), **Rosette** (`.rosette`), **Mark** (`.mark`), **Drop cap** (`.post-body--cap`), **Icon actions** (`.icon-row`, `.icon-btn`, with `kit/icon-controls.js`), **Closing note** (`.closing-note`), **Dated list** (`.dated-list`), **Summary list** (`.summary-list`), **Output panel** (`.output`, with `kit/output.js`), **Code block** (`pre`, `.pre--bar`, `.pre-cmd`, `.pre-err`, `.tok-*`), **Ellipse** (`.ellipse`, `.ellipse--solid`), **Number circle** (`.num-circle`, `.num-circle--solid`, `.num-circle--accent`), **Activity indicators** (`.eq`, `.spinner`), **Image** (`.media`, `.figure`), **Hero** (`.hero`, `.hero--split`, `.hero--wide`), **Page title** (`.page-title`, `.page-title--bar`), **CTA** (`.cta`), **Card grid** (`.card-grid`, `.card`), **Post list** (`.post-list`), **Post feature** (`.post-feature`), **Pager** (`.pager`), **Post header** (`.post-header`, `.post-meta`), **Post body** (`.post-body`), **Post nav** (`.post-nav`), **Footer** (`.site-footer`, `.site-footer--bar`).
|
|
159
|
+
|
|
160
|
+
## With Tailwind v4
|
|
161
|
+
|
|
162
|
+
This is the default setup, and the default.
|
|
163
|
+
|
|
164
|
+
1. Install `tailwindcss` and `@tailwindcss/vite`, and add the plugin in `astro.config.mjs`:
|
|
165
|
+
```js
|
|
166
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
167
|
+
export default defineConfig({ vite: { plugins: [tailwindcss()] } });
|
|
168
|
+
```
|
|
169
|
+
2. Make one global stylesheet, e.g. `src/styles/global.css`, and import it in your layout:
|
|
170
|
+
```css
|
|
171
|
+
@import "@agentic-design-kits/verdigris/tailwind.css";
|
|
172
|
+
/* site-only CSS below */
|
|
173
|
+
```
|
|
174
|
+
Don't also `@import "tailwindcss"` or `theme.css` anywhere else; `tailwind.css` does both.
|
|
175
|
+
3. Pass `barHeight="5rem"` to `BaseLayout` if the site has a fixed bottom bar.
|
|
176
|
+
|
|
177
|
+
**Who does what.** Components with state, motion or several parts are kit classes (`.row`, `.chip`, `.panel`, `.toast`, `.hero`, `.media`, the type styles). Page layout is utilities in the markup: grids, spacing, widths, which column a piece sits in. Mix them freely: `class="row grid gap-y-1 md:grid-cols-[3rem_1fr_1.75rem]"`.
|
|
178
|
+
|
|
179
|
+
**Layers.** `theme.css` sits in Tailwind's `components` layer, so a utility always beats a kit class it is paired with (`class="field mb-7"` sets the margin). Your own unlayered CSS beats both.
|
|
180
|
+
|
|
181
|
+
**Token utilities.** Use these instead of raw colors, so light and dark both work: `bg-paper`, `text-ink`, `text-muted`, `border-rule`, `bg-bar`, `text-bar-ink`, `bg-accent` (large shapes only), `text-accent-text` (small accent text on the page), `text-accent-bright` (small accent text on `bar`), `text-copper-text` (copper labels and small ornament on the page), `text-copper-bright` (copper on `bar`), `text-copper` and `bg-copper` (large ornament only), `bg-row-hover`, `bg-row-active`, `text-row-fg`, and `var(--bar-h)` as `h-(--bar-h)` / `pb-(--bar-h)`. Never `text-red-600`, `bg-black` or Tailwind's grey scale.
|
|
182
|
+
|
|
183
|
+
**Defaults that line up.** Tailwind's `sm` (640px) and `md` (768px) breakpoints are the kit's. Preflight clears margins on `p`, `h1`–`h6` and lists, so set spacing with utilities (`mb-5`, `[&>p]:mb-5` on a block of prose). Tailwind v4 drops the hand cursor on buttons; `theme.css` puts it back.
|
|
184
|
+
|
|
185
|
+
`kit/tailwind.css` maps the tokens like this:
|
|
186
|
+
|
|
187
|
+
```css
|
|
188
|
+
@theme inline {
|
|
189
|
+
--color-paper: var(--paper);
|
|
190
|
+
--color-ink: var(--ink);
|
|
191
|
+
--color-muted: var(--muted);
|
|
192
|
+
--color-rule: var(--rule);
|
|
193
|
+
--color-accent: var(--accent);
|
|
194
|
+
--color-accent-ink: var(--accent-ink);
|
|
195
|
+
--color-accent-text: var(--accent-text);
|
|
196
|
+
--color-accent-bright: var(--accent-bright);
|
|
197
|
+
--color-bar: var(--bar);
|
|
198
|
+
--color-bar-ink: var(--bar-ink);
|
|
199
|
+
--color-row-hover: var(--row-hover-bg);
|
|
200
|
+
--color-row-active: var(--row-active-bg);
|
|
201
|
+
--color-row-fg: var(--row-fg);
|
|
202
|
+
--color-row-edge: var(--row-edge);
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Do not
|
|
207
|
+
|
|
208
|
+
- Add shadows, gradients, rounded corners or boxes around inputs.
|
|
209
|
+
- Use capitals: no `text-transform: uppercase`, no small caps, no typed all-caps labels.
|
|
210
|
+
- Set text under 18px (code aside), or light weight under 1.75rem.
|
|
211
|
+
- Use a second family for anything but code, or tint the background behind code.
|
|
212
|
+
- Use the code colors outside `pre` and `code`.
|
|
213
|
+
- Use plain `accent` or `copper` for small text, or copper to mark an action.
|
|
214
|
+
- Use more than one plate or fleuron rule on a page, or put text on the rosette.
|
|
215
|
+
- Let ornament say something no text says.
|
|
216
|
+
- Use images as filler or background, round or frame them, or set text straight on them.
|
|
217
|
+
- Let text keep moving forever, or move at all with Reduce Motion on.
|
|
218
|
+
- Hide focus rings or drop the hand cursor.
|
|
219
|
+
- Use an icon font or a brand's colors.
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
Cormorant by Christian Thalmann, SIL Open Font License 1.1: https://github.com/CatharsisFonts/Cormorant. Free on any site as long as `fonts/Cormorant-LICENSE.txt` stays with it.
|
|
224
|
+
|
|
225
|
+
Source Code Pro by Paul D. Hunt for Adobe, SIL Open Font License 1.1: https://github.com/adobe-fonts/source-code-pro. Free on any site as long as `fonts/SourceCodePro-LICENSE.txt` stays with it.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Danny Englander
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/rosette-dark.svg" width="360" alt="An engine-turned rosette, line work in verdigris and copper on a dark ground.">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center"><a href="https://verdigris.ch">verdigris.ch</a></p>
|
|
6
|
+
|
|
7
|
+
# Verdigris Design Kit
|
|
8
|
+
|
|
9
|
+
A design kit your AI agent can implement, for Astro and Tailwind v4: Swiss Minimal's strict grid, set in Cormorant, in verdigris and copper, with ornament that earns its place. No capitals anywhere, for accessibility.
|
|
10
|
+
|
|
11
|
+
It is built on [Swiss Minimal](../README.md) and lives in a folder of its repository. See [Built on Swiss Minimal](#built-on-swiss-minimal).
|
|
12
|
+
|
|
13
|
+
It is on npm:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @agentic-design-kits/verdigris
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
@import "@agentic-design-kits/verdigris/tailwind.css";
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Swiss Minimal comes with it. To update, run `npm update`. Versions follow the release date: `2026.1113.0` is the first release on 13 November 2026.
|
|
24
|
+
|
|
25
|
+
- [`DESIGN.md`](DESIGN.md): the rules (color, type, layout, images, motion, accessibility, Tailwind setup).
|
|
26
|
+
- [`COMPONENTS.md`](COMPONENTS.md): markup and rules for each component.
|
|
27
|
+
- [`TOOLING.md`](../TOOLING.md) (Swiss Minimal's, for both kits): recipes for accessibility tests (Playwright and axe) and date-tagged releases, and a manual checklist.
|
|
28
|
+
- [`AGENTS.md`](AGENTS.md): notes for AI agents (also `CLAUDE.md`): working on this repository, starting a new site with the kit, and restyling an existing one.
|
|
29
|
+
- [`kit/`](kit): what makes this Verdigris: `tailwind.css`, `verdigris.css`, `fonts.css` and the components written here (`BaseLayout`, `Mark`, `Rosette`). `kit/shared/` passes on Swiss Minimal's components and scripts, so a site imports all of them from `@agentic-design-kits/verdigris`.
|
|
30
|
+
- [`fonts/`](fonts): Cormorant (SIL OFL 1.1). Source Code Pro, for code, comes with Swiss Minimal.
|
|
31
|
+
- [`demo/`](demo): a small Astro site that uses every component. It installs the kit as the `@agentic-design-kits/verdigris` package, through the npm workspace in the repository's root [`package.json`](../package.json).
|
|
32
|
+
|
|
33
|
+
## Run the demo
|
|
34
|
+
|
|
35
|
+
Needs Node 24 or later (`demo/.nvmrc` pins 24).
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cd demo
|
|
39
|
+
nvm use
|
|
40
|
+
npm install
|
|
41
|
+
npm run dev
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Test the demo
|
|
45
|
+
|
|
46
|
+
Builds the demo and scans every page, the menu tabs and the drawer for WCAG 2.1 AA problems in light and dark. See [`TOOLING.md`](../TOOLING.md).
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cd demo
|
|
50
|
+
npx playwright install chromium
|
|
51
|
+
npm test
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Built on Swiss Minimal
|
|
55
|
+
|
|
56
|
+
The base stylesheet, the scripts and most components are Swiss Minimal's. Verdigris uses them as the `@agentic-design-kits/swiss-minimal` package and adds its own look on top. Both kits are in this repository, Swiss Minimal at the root and Verdigris in `verdigris/`, so Verdigris always uses the Swiss Minimal beside it.
|
|
57
|
+
|
|
58
|
+
- **The Verdigris look** lives in `kit/verdigris.css` and `kit/fonts.css`. Change those here.
|
|
59
|
+
- **A fix to a shared component** goes in Swiss Minimal's [`kit/`](../kit), and both demos have it at once.
|
|
60
|
+
- **When Swiss Minimal adds or removes a component or script,** run this at the repository's root, so Verdigris passes it on:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm run shared
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The full rules are in "Verdigris is built on Swiss Minimal" in [`AGENTS.md`](AGENTS.md).
|
|
67
|
+
|
|
68
|
+
## Releases
|
|
69
|
+
|
|
70
|
+
Verdigris is released together with Swiss Minimal, by `npm run release` at the repository's root. See Releases in the root [`README`](../README.md). Before releasing something a visitor would notice, add it at the top of `demo/src/data/features.ts`.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT: see [`LICENSE`](../LICENSE). The fonts in [`fonts/`](fonts) keep their own license, SIL OFL 1.1.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2015 the Cormorant Project Authors (github.com/CatharsisFonts/Cormorant)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
https://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Verdigris's layout: Swiss Minimal's BaseLayout with this theme's text face preloaded in place
|
|
3
|
+
// of Swiss Minimal's. The props and slots are the same; see that file for what each one does.
|
|
4
|
+
// It does not import any CSS itself: import your site's global stylesheet (which starts with
|
|
5
|
+
// `@import "@agentic-design-kits/verdigris/tailwind.css";`) in your own layout.
|
|
6
|
+
import Base from '@agentic-design-kits/swiss-minimal/astro/BaseLayout.astro';
|
|
7
|
+
import type { ComponentProps } from 'astro/types';
|
|
8
|
+
import font from './font';
|
|
9
|
+
|
|
10
|
+
type Props = ComponentProps<typeof Base>;
|
|
11
|
+
const { panelTabs = [], preloadFont = font, ...rest } = Astro.props;
|
|
12
|
+
// Slots that Swiss Minimal's layout tests for (`Astro.slots.has`) are passed on only when they
|
|
13
|
+
// are filled, so an empty one still counts as empty there.
|
|
14
|
+
const mark = Astro.slots.has('mark') ? await Astro.slots.render('mark') : null;
|
|
15
|
+
const tabs = await Promise.all(
|
|
16
|
+
panelTabs
|
|
17
|
+
.filter((t) => Astro.slots.has(t.id))
|
|
18
|
+
.map(async (t) => ({ id: t.id, html: await Astro.slots.render(t.id) })),
|
|
19
|
+
);
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<Base {...rest} panelTabs={panelTabs} preloadFont={preloadFont}>
|
|
23
|
+
<slot name="head" slot="head" />
|
|
24
|
+
{mark !== null && <Fragment slot="mark" set:html={mark} />}
|
|
25
|
+
<slot name="topbar" slot="topbar" />
|
|
26
|
+
{tabs.map((t) => <Fragment slot={t.id} set:html={t.html} />)}
|
|
27
|
+
<slot name="panel" slot="panel" />
|
|
28
|
+
<slot name="overlays" slot="overlays" />
|
|
29
|
+
<slot />
|
|
30
|
+
<slot name="footer" slot="footer" />
|
|
31
|
+
</Base>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
// A drawn mark: Verdigris's one icon set, in one pen (see Iconography in DESIGN.md). aria-hidden,
|
|
3
|
+
// because a mark is either beside a label that says it or inside a control with an aria-label.
|
|
4
|
+
export const marks = {
|
|
5
|
+
menu: 'M3 7h18M3 12h13M3 17h18',
|
|
6
|
+
search: 'M10.5 4a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM15.2 15.2 20.5 20.5',
|
|
7
|
+
next: 'M2.5 12H21M16 7c.6 2.4 2.2 4.1 5 5-2.8.9-4.4 2.6-5 5',
|
|
8
|
+
open: 'M6 18 18 6M9 6h9v9',
|
|
9
|
+
close: 'M6 6l12 12M18 6 6 18',
|
|
10
|
+
journal: 'M12 6v14M12 6C10 4.500 7 4 4 4v14c3 0 6 .5 8 2 2-1.500 5-2 8-2V4c-3 0-6 .5-8 2Z',
|
|
11
|
+
write: 'M12 21v-6M9.500 15h5L18 8.500 12 2.500 6 8.500 9.500 15ZM12 2.500V10',
|
|
12
|
+
letter: 'M3 6h18v12H3zM3 7l9 7 9-7',
|
|
13
|
+
save: 'M6.500 3h11v18L12 16.500 6.500 21Z',
|
|
14
|
+
leaf: 'M4 20C4 11 9.500 4.500 20 4c.3 10.500-6 16-16 16ZM4 20 14 10',
|
|
15
|
+
date: 'M4 5.500h16v15H4zM4 10h16M8.500 3v4M15.500 3v4',
|
|
16
|
+
place: 'M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18ZM3 12h18M12 3c3.400 2.700 3.400 15.300 0 18M12 3c-3.400 2.700-3.400 15.300 0 18',
|
|
17
|
+
} as const;
|
|
18
|
+
interface Props {
|
|
19
|
+
name: keyof typeof marks;
|
|
20
|
+
class?: string;
|
|
21
|
+
}
|
|
22
|
+
const { name, class: className } = Astro.props;
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<svg class:list={['mark', className]} viewBox="0 0 24 24" aria-hidden="true"><path d={marks[name]}></path></svg>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
// The engraved rosette (see Ornament in DESIGN.md): line work in the two metals, for a page with
|
|
3
|
+
// no picture of its own. Drawn at build time, so it follows the theme. Decorative: aria-hidden.
|
|
4
|
+
function wave(radius: number, depth: number, waves: number, phase: number, points: number) {
|
|
5
|
+
const at: string[] = [];
|
|
6
|
+
for (let i = 0; i < points; i += 1) {
|
|
7
|
+
const angle = (2 * Math.PI * i) / points;
|
|
8
|
+
const r = radius + depth * Math.sin(waves * angle + phase);
|
|
9
|
+
at.push(`${(300 + r * Math.cos(angle)).toFixed(1)} ${(300 + r * Math.sin(angle)).toFixed(1)}`);
|
|
10
|
+
}
|
|
11
|
+
return `M${at.join('L')}Z`;
|
|
12
|
+
}
|
|
13
|
+
const band = (curves: number, radius: number, depth: number, waves: number, points: number) =>
|
|
14
|
+
Array.from({ length: curves }, (_, i) => wave(radius, depth, waves, (2 * Math.PI * i) / curves, points)).join('');
|
|
15
|
+
interface Props {
|
|
16
|
+
class?: string;
|
|
17
|
+
spin?: boolean; // Turns slowly, with a switch to stop it (WCAG 2.2.2). Never with Reduce Motion.
|
|
18
|
+
}
|
|
19
|
+
const { class: className, spin = false } = Astro.props;
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<div class:list={['rosette', { 'rosette--spin': spin }, className]}>
|
|
23
|
+
<div class="rosette-disc">
|
|
24
|
+
<svg viewBox="0 0 600 600" aria-hidden="true">
|
|
25
|
+
<path class="rosette-outer" d={band(12, 262, 24, 18, 216)}></path>
|
|
26
|
+
<path class="rosette-inner" d={band(12, 150, 46, 9, 126)}></path>
|
|
27
|
+
<path class="rosette-core" d={band(8, 52, 20, 6, 72)}></path>
|
|
28
|
+
</svg>
|
|
29
|
+
</div>
|
|
30
|
+
{spin && <label class="choice rosette-switch"><input class="switch" type="checkbox" role="switch" checked /> Turn the rosette slowly</label>}
|
|
31
|
+
</div>
|