styling
Three layers, composable
1. Sensible defaults. Drop a component in — it works.
2. CSS custom properties on any ancestor re-skin every component below
(tokens cross the shadow boundary by inheritance).
3. ::part() for piercing the shadow to target specific
internals when tokens don't cover it.
Default (tier 1)
Tier 2 — swap tokens
Each example wraps the same <atproto-post> in a <div>
that sets different custom properties. No JS, no selector wizardry — the component inherits
the tokens and re-skins itself.
Warm accent, small radii, serif
--atproto-accent: #ea580c;
--atproto-accent-soft: rgba(234,88,12,0.12);
--atproto-radius: 4px;
--atproto-font: Georgia, serif; Compact density
--atproto-space-3: 0.4rem;
--atproto-space-4: 0.6rem;
--atproto-font-size: 0.85rem;
--atproto-font-size-sm: 0.78rem;
--atproto-line-height: 1.35; Elevated (shadow, borderless)
--atproto-shadow: 0 10px 40px -12px rgba(17,133,254,0.35);
--atproto-border: transparent;
--atproto-radius: 16px; Editorial serif
--atproto-font: "Iowan Old Style", Georgia, serif;
--atproto-font-size: 1rem;
--atproto-letter-spacing-tight: -0.015em;
--atproto-radius: 2px; Tier 3 — pierce with ::part()
When tokens don't reach deep enough, use ::part() to target the component's
named internals. Every high-level component documents its parts on its page.
atproto-post::part(article) {
box-shadow: var(--atproto-shadow-lg);
border-color: var(--accent);
}
atproto-post::part(avatar) { border-radius: 4px }
atproto-post::part(counts) {
background: var(--atproto-accent-soft);
padding: 0.5rem 0.75rem;
border-radius: 8px;
} All tokens
Every named custom property the library reads. Override at any ancestor — tokens cross
the shadow boundary by inheritance, so setting --atproto-accent on
body restyles every component below.
Colors
Base palette + interactive states + semantic.
| Token | Purpose |
|---|---|
--atproto-bg | Element background |
--atproto-text | Primary text |
--atproto-muted | Secondary text |
--atproto-accent | Links, counts, focus |
--atproto-accent-soft | Tints (pill bgs, hover rings) |
--atproto-accent-hover | Link hover color |
--atproto-hover-bg | Button/card hover background |
--atproto-focus-ring | Focus outline color |
--atproto-border | Card borders, separators |
--atproto-subtle | Lower-contrast fill for stripes |
--atproto-link | Explicit link color (defaults to accent) |
--atproto-link-visited | Visited link color |
--atproto-error | Error state accent |
--atproto-warning | Warning accent (gate-badge) |
--atproto-success | Success accent |
Typography
Font stacks, size scale, weight, rhythm.
| Token | Purpose |
|---|---|
--atproto-font | Sans stack |
--atproto-font-mono | Mono stack (code, CIDs) |
--atproto-font-size-xs | 0.75rem |
--atproto-font-size-sm | 0.85rem |
--atproto-font-size | 0.95rem (body default) |
--atproto-font-size-lg | 1.15rem |
--atproto-font-size-xl | 1.5rem (profile name) |
--atproto-line-height | 1.5 (body) |
--atproto-line-height-tight | 1.25 (headings) |
--atproto-letter-spacing-tight | -0.01em (headings) |
--atproto-font-weight | 400 |
--atproto-font-weight-semibold | 600 |
--atproto-font-weight-bold | 700 |
Spacing + radii + sizes
Consistent scales for layout rhythm.
| Token | Purpose |
|---|---|
--atproto-space-1..6 | 0.25 / 0.5 / 0.75 / 1 / 1.25 / 1.5 rem |
--atproto-radius-sm | 4px (tight inputs) |
--atproto-radius-inner | 8px (inside cards) |
--atproto-radius | 12px (default card) |
--atproto-radius-lg | 16px (outer containers) |
--atproto-radius-pill | 999px (badges, chips) |
--atproto-avatar-size{,-sm,-lg} | 40 / 24 / 80 px |
--atproto-max-width | 560px (content cap) |
Elevation + motion
Shadows are opt-in (default flat); transition drives every hover.
| Token | Purpose |
|---|---|
--atproto-shadow | Applied shadow (default: none) |
--atproto-shadow-sm | 1px preset |
--atproto-shadow-md | 4px preset |
--atproto-shadow-lg | 10px spread preset |
--atproto-transition-duration | 0.15s |
--atproto-transition-easing | ease |
--atproto-transition | Bundled color/bg/border/shadow/transform transition |