jeen design system — v0.6.4
Live demos, props, usage rules, and when-not-to for every system component.
Calls to action — page CTAs, form submits, navigation triggers. Two variants cover the two surface contexts: primary for light surfaces, secondary for dark surfaces only. Renders as <a> when href is provided, <button> otherwise.
| Prop | Type | Default | Notes |
|---|---|---|---|
variant | "primary" | "secondary" | "primary" | Controls appearance and surface context |
label | string | — | Button text content |
href | string | undefined | If set, renders as <a> |
class | string | — | Forwarded to the root element |
Renders as <a> when href is provided.
| Variant | Class | Surface | Behaviour |
|---|---|---|---|
primary | .hero-cta-primary | Light | Yellow bg · hard shadow · lifts on hover |
secondary | .hero-cta-secondary | Dark only | Glass outline · white text · dark surface only |
| Rule | |
|---|---|
| ✅ | Use primary for the main CTA on light backgrounds (hero, playground, about) |
| ✅ | Use secondary on --bg-dark surfaces only — hero, footer dark zones |
| ✅ | Provide href when the action navigates; omit for form submits and JS-triggered actions |
| ❌ | Never use primary on dark surfaces — yellow on black fails the visual hierarchy |
| ❌ | Never use secondary on light surfaces — transparent outline with white text is invisible |
| ❌ | Never stack two primary buttons side-by-side — one CTA per focal area |
Decorative label/tag pill for categorising and annotating content. Maps a variant prop to an existing homepage.css class — no new CSS is introduced. Surface context matters: verify contrast before placing a badge variant on a new surface.
| Prop | Type | Default | Notes |
|---|---|---|---|
variant | "tag" | "diary-tag" | "work-tag" | "skill" | "coming-soon" | "tag" | Maps to a homepage.css class |
label | string | undefined | Text content; coming-soon renders its own label + dot if omitted |
| Variant | Class | Surface | Used in |
|---|---|---|---|
tag | .play-card-tag | Light | Playground experiment cards |
diary-tag | .diary-tag | Light | Diary card category labels |
work-tag | .work-tag | Dark | Work section row tags |
skill | .about-skill-tag | Dark | About section skills list |
coming-soon | .work-coming-soon-badge | Dark | Work + Playground coming-soon items |
| Rule | |
|---|---|
| ✅ | Pick variant by where the badge appears — dark-surface variants (work-tag, skill) are designed for --bg-dark contexts only |
| ✅ | Use coming-soon inline in titles — it renders its own dot + label |
| ❌ | Never use a badge as an interactive element — it has no hover or focus state |
| ❌ | Never invent new badge variants — extend the token set in homepage.css only if a genuine new context arises |
| ❌ | Never use work-tag or skill on light backgrounds — contrast will fail |
Outer container for experiment and diary content blocks. Uses a slot — no internal template is imposed. Renders as <a> when href is provided. Inner content uses existing homepage.css classes.
| Prop | Type | Default | Notes |
|---|---|---|---|
variant | "default" | "accent" | "diary" | "default" | Controls layout and surface treatment |
href | string | undefined | If set, renders as <a> with hover lift |
class | string | — | Forwarded to root element |
slot | HTML | — | All card content goes in the default slot |
Light background, border, hard offset shadow. The standard experiment card.
Yellow accent background. Used for highlighted or featured experiments.
Example diary entry title
| Variant | Class | Layout | Behaviour |
|---|---|---|---|
default | .play-card | Vertical · play card | Light bg · border · hard shadow · lifts on hover |
accent | .play-card.play-card--accent | Vertical · play card | Yellow bg · same shape as default · featured experiments |
diary | .diary-card | Horizontal · date + content + arrow | Light bg · border · diary list rows |
All variants render as <a> when href is provided. Hover: translateY(-2px) + shadow-hover.
| Rule | |
|---|---|
| ✅ | Use default for experiment/playground cards in the grid |
| ✅ | Use accent sparingly — one featured card per grid at most |
| ✅ | Use diary for diary list rows only — it has a fixed horizontal layout |
| ✅ | Provide href whenever the card is clickable — it semantically becomes an <a> |
| ❌ | Never nest a card inside another card |
| ❌ | Never use the diary variant in the experiment grid — layouts are incompatible |
Semantic anchor (<a>) with surface-appropriate styling. Always renders as a link. Pick variant by surface context — each variant is designed for a specific background. Hover each demo to test the interaction.
| Prop | Type | Default | Notes |
|---|---|---|---|
href | string | — | Required — destination URL or anchor |
variant | "default" | "on-dark" | "nav" | "back" | "footer-nav" | "default" | Pick by surface context |
external | boolean | false | Adds target="_blank" rel="noopener noreferrer" |
| Variant | Class | Surface | Rest token | Hover |
|---|---|---|---|---|
default | (global a) | Light | --fg-primary | box-shadow floods dark · yellow text |
on-dark | .hero-social-link | Dark | --fg-on-dark-primary | box-shadow floods light · dark text |
nav | .nav-link | Dark | --fg-on-dark-primary | color → --fg-on-dark-accent (yellow) |
back | .back-link | Dark | --fg-on-dark-label | color → --fg-on-dark-primary |
footer-nav | .footer-nav-link | Dark | --fg-on-dark-label | color → --fg-on-dark-primary |
| Rule | |
|---|---|
| ✅ | Always pick variant by surface — dark-surface variants will be invisible on light backgrounds |
| ✅ | Add external prop for any link opening a new tab — it handles rel="noopener noreferrer" |
| ✅ | Use nav only in the global header nav — it has pill shape specific to that context |
| ✅ | Use back for breadcrumb-style back links on dark surfaces (topbar, article header) |
| ❌ | Never use default on dark surfaces — the box-shadow underline floods dark, making it invisible |
Circular icon-only button. 32×32px, icon at 20×20px. Default state is transparent — no background, icon inherits --fg-primary. Hover and press show --bg-accent-dim (yellow-dim). Use for compact actions — copy, share, bookmark — where a full-width button would be too heavy.
| Prop | Type | Required | Notes |
|---|---|---|---|
label | string | ✅ | Becomes aria-label — required for screen readers |
class | string | ❌ | Appended to .icon-btn |
slot | Lucide icon or inline SVG | ✅ | Pass at size=20 |
| Rule | |
|---|---|
| ✅ | Always pass label — it is the only text available to screen readers |
| ✅ | Pass the icon at size=20 — this keeps the 6px padding on each side within the 32px circle |
| ✅ | Use for single, compact actions near content (copy, bookmark, share, dismiss) |
| ❌ | Never use without a label — the button has no visible text |
| ❌ | Never pass text into the slot — this component is icon-only |
| ❌ | Never use as a primary CTA — use Button with a label instead |
There is no src/components/system/Input.astro yet. Input styles exist in design-system.css as demo-only classes (.ds-input, .ds-ask-bar, .ds-ask-input) but these are scoped to the DS documentation page and are not a shipped component.
When Input is promoted to a system component, it will live at src/components/system/Input.astro with props for type, label, placeholder, error, disabled, and variant (default | ask-bar). Tokens and states are already defined — see Foundations → Borders and Colors for relevant values.