Theming
Everything visual in astro-ink reads from CSS custom properties. Re-skin the whole library without touching a component file.
Import order
Add these imports to your layout frontmatter:
import "astro-ink/styles/tokens.css";
import "astro-ink/styles/base.css";
// optional:
import "astro-ink/styles/themes/dark.css";Global tokens
Set any token on :root (or any container) to change the library. One change re-skins every component that reads it.
:root {
--ink-accent: #0f766e; /* teal instead of vermillion */
--ink-radius-md: 0.5rem; /* a bit rounder */
--ink-font-sans: "Atkinson Hyperlegible", sans-serif;
}Component tokens
Each component reads its own token first, falling back to the global one. Change only what you need.
:root {
--ink-button-radius: 999px; /* pill buttons, everything else unchanged */
}Dark mode
Import astro-ink/styles/themes/dark.css and set the theme with an attribute. The toggle logic belongs to your app; this site's toggle is a working example (view source of the header).
document.documentElement.dataset.theme = "dark";Custom theme template
A theme is one CSS file of token overrides. Copy this template, adjust the values, import it after the tokens file.
/* my-theme.css */
:root {
--ink-bg: #f5f5f4;
--ink-surface: #ffffff;
--ink-ink: #1c1917;
--ink-muted: #57534e;
--ink-accent: #7c2d12;
--ink-accent-ink: #ffffff;
--ink-focus-color: var(--ink-accent);
}
/* Required for AA contrast: verify every color pair you change
reaches 4.5:1 for normal text, 3:1 for large text. */Authoring rules
A custom theme wins over component styles without fighting them. Three rules keep it that way:
- Tokens only: override
--ink-*custom properties, never restyle a component class. - No
!important: component CSS never uses it, so your token values apply with normal specificity. - Low specificity: one selector (such as
:rootor[data-theme="mine"]) beats none, and loses to nothing. Layer order does the rest: import your file aftertokens.css.
Shape tokens
Every component that draws a pill, circle, thumb, dot, or ring reads its own radius token first and falls back to its default shape. Set one token to reshape one component, or zero them all for square corners (the brutal preset does exactly this):
:root {
--ink-radius-sm: 0;
--ink-radius-md: 0;
--ink-radius-lg: 0;
--ink-alert-radius: 0;
--ink-avatar-radius: 0;
--ink-avatargroup-radius: 0;
--ink-badge-radius: 0;
--ink-button-radius: 0;
--ink-carousel-radius: 0;
--ink-carousel-dot-radius: 0;
--ink-chip-radius: 0;
--ink-color-swatch-radius: 0;
--ink-fab-radius: 0;
--ink-highlight-radius: 0;
--ink-input-radius: 0; /* shared by Field, OtpInput, TagInput, TransferList, ColorPicker panel */
--ink-loading-radius: 0;
--ink-paper-radius: 0;
--ink-progress-radius: 0;
--ink-radio-radius: 0;
--ink-rating-radius: 0;
--ink-scrollbar-radius: 0;
--ink-skeleton-radius: 0;
--ink-slider-radius: 0;
--ink-slider-thumb-radius: 0;
--ink-speed-radius: 0;
--ink-spinner-radius: 0;
--ink-stepper-radius: 0;
--ink-switch-radius: 0;
--ink-switch-thumb-radius: 0;
--ink-timeline-radius: 0;
}Presets
Eight preset themes ship with the library: the default Paper and Ink light theme, the dark theme, plus midnight(dark-first electric), brutal (neo-brutalist light),forest (green-tinted light), ocean (deep-sea dark), ember (warm hearth dark), plum (violet light), and slate (graphite dark with amber accent). Each preset is one import of token overrides, one theme per page:
import "astro-ink/styles/tokens.css";
import "astro-ink/styles/base.css";
// pick one preset (or none for the default light theme):
import "astro-ink/styles/themes/dark.css";
import "astro-ink/styles/themes/midnight.css";
import "astro-ink/styles/themes/brutal.css";
import "astro-ink/styles/themes/forest.css";
import "astro-ink/styles/themes/ocean.css";
import "astro-ink/styles/themes/ember.css";
import "astro-ink/styles/themes/plum.css";
import "astro-ink/styles/themes/slate.css";document.documentElement.dataset.theme = "midnight";
// or: "light", "dark", "brutal", "forest", "ocean", "ember", "plum", "slate"Try them on this page (demo only, persisted like the header toggle). Each card applies its theme on click: