Skip to content

Releases: glw907/cairn-cms

v0.91.1: restore the admin-sheet classes 0.91.0 dropped

Choose a tag to compare

@glw907 glw907 released this 30 Jul 03:47

0.91.1

Fixed

  • The shipped admin sheet restores the nineteen class rules 0.91.0 silently dropped
    (#12). The sheet compiles from Tailwind
    scanning cairn's own source, and the 0.91.0 window migrated cairn's tree off the named type
    steps onto the grammar roles, so Tailwind tree-shook the newly unused utilities out of the
    compiled sheet: text-sm, text-xs, text-lg, text-base, text-2xl, text-3xl, gap-6,
    tracking-tight, badge-ghost, and ten bracketed arbitrary sizes (text-[0.625rem] through
    text-[1.0625rem]). Consumer admin markup resolves its classes from that sheet, so markup
    riding any of the nineteen rendered unstyled on 0.91.0 while the upgrade guide promised no
    rendering change. All nineteen are back through a labeled compatibility safelist in the sheet's
    compile input, and the sheet's class inventory is now a gated contract: a snapshot test diffs
    the built sheet's inventory against a committed snapshot, so a class can leave the shipped
    sheet only as a deliberate, changelog-carried act.

    Consumers must: nothing, coming from 0.90.1 or earlier; the sheet again carries every class
    it did there. Coming from 0.91.0, upgrade and any custom admin markup that lost its styling
    (most commonly text-sm) styles again with no markup change.

v0.91.0: the design language, enforced — grammar tokens, cairn-audit, and the admin-screens skill

Choose a tag to compare

@glw907 glw907 released this 29 Jul 22:26

The design-infrastructure initiative's window: three passes held under one cut. The admin's design language becomes named structure (the grammar tokens and role utilities), mechanically checkable (the cairn-audit bin, nine static and eleven rendered rules, the norms manifest), and loadable by an agent (the packaged cairn-admin-screens skill, installed by cairn-doctor --fix). Two Consumers must: lines below; the type-grammar adoption recipe is in Upgrade cairn.

Added

  • The admin's structural type and spacing scale is now named: ten CSS custom properties
    (--cairn-type-title/subtitle/body/meta/label/chip, --cairn-gap-label/control/group/section)
    declared once in cairn-admin.css, outside the light and dark theme blocks, so a heading's size
    or a control-to-control gap holds across both themes. Ten role utilities
    (type-title/type-subtitle/type-body/type-meta/type-label/type-chip,
    gap-control/gap-label/gap-group/gap-section) are the supported way to reach a token from
    markup; each sets one property and nothing else. All ten ship in the compiled admin stylesheet
    whether or not cairn's own screens use them, so a role is available to a custom admin route on
    the strength of the documentation alone. See Admin grammar
    tokens
    .

  • src/lib/admin-toolkit and the built-in engine admin screens migrate their on-scale type and
    spacing literals to the new role utilities and tokens, pixel-identically: the committed
    admin-visual snapshots do not move. The toolkit's scoped styles carry the measured literal as a
    var() fallback, since those components can mount outside the admin theme root; engine screens
    reference the token bare.

  • The palette/grammar boundary is now a documented contract: a site re-tunes the palette tokens
    (--color-*) to its own brand and never redeclares a grammar token, since grammar names
    structure and palette is the brand layer.

  • The type scale closes at seven roles, each carrying a ruled line-height, and the grammar
    inventory grows from ten custom properties to eighteen. Every type role gains a paired
    --cairn-type-<role>--leading token, and a seventh role, --cairn-type-heading with its
    type-heading utility (18px, bold, the display face), unifies the two heading recipes the admin
    had been running side by side. Each type-* utility now declares
    line-height: var(--tw-leading, var(--cairn-type-<role>--leading)), so a leading-* utility
    still composes over a role rather than being overridden by it. cairn's own admin screens migrate
    onto the closed scale: 129 named type steps, 120 twelve-pixel sites resolved onto type-meta or
    type-label by the relationship each site expresses, both heading families onto type-heading,
    and the stray 9.6px and 11.2px slips onto type-chip and type-label. Five sites keep a value
    off the scale and each carries a counted suppression directive naming why: the three wordmark
    sites (the K4 keming fix) and the editor canvas and its document title, which set the editor's
    own type scale rather than the admin chrome's. The losing heading family and part of the
    twelve-pixel set change size by ruling, so the admin-visual baselines were regenerated once on
    CI and read by eye. See Admin grammar tokens.

  • Two container role utilities, card-shell and card-shadow, are the supported replacement for
    hand-copying the admin's card-shell class string
    (rounded-box border border-[var(--cairn-card-border)] bg-base-100, optionally with
    shadow-[var(--cairn-shadow)]). card-shell carries the shell's radius, hairline border, and
    fill; card-shadow carries its elevation, kept separate because a surface already nested inside
    a shadowed container takes card-shell alone. Both ship in the compiled admin stylesheet
    regardless of cairn's own usage, the same safelist discipline as the eleven grammar role utilities.
    cairn's own admin components migrate every verbatim shell site onto the new utilities,
    pixel-identically. See Admin grammar tokens.

  • The package ships a new bin, cairn-audit, the design-language audit. An install puts it on the
    project's path, and npx cairn-audit runs the static rules over the admin surfaces. The
    substrate is svelte/compiler for markup and the built cairn-admin.css for resolution, never a
    regex over source text: a class token is matched exactly, so text-base (the size utility) and
    text-base-content (the daisyUI color utility) can never read as the same class, and a class
    written as an array, an object, a template literal, or a class: directive is seen the same as
    one written in a plain attribute. Configuration is one optional cairn-audit.config.json whose
    every key defaults, so a site that has written no config still gets a meaningful run; a config
    that names a scan path the tree does not have fails the run instead of quietly auditing less.
    See The cairn-audit CLI.

  • Nine static rules ship, all error tier. no-uncompiled-class: every markup class token compiles
    into the built sheet or is a name the component's own <style> block defines. type-scale:
    every font size a text-sizing token resolves to comes from a --cairn-type-* role.
    gap-scale: an arbitrary margin, padding, or gap literal resolves to a --cairn-gap-* role or
    lands on Tailwind's own spacing grid. stock-default-hazards: four stock daisyUI patterns
    cairn's own recipes replace (badge-ghost, the focus-driven bare .dropdown, native disabled
    on a guarded button, a flat base-300 card border), each finding citing the refuted alternative
    on record. token-colors: no raw hex, rgb(), named-color, or pure-achromatic literal outside a
    declared palette site. grammar-boundary: consumer CSS never redeclares a grammar token.
    focus-parity: every hand-authored :hover selector has a matching :focus-visible or
    :focus-within. motion-band: every declared duration lands in the admin's 150 to 250ms band,
    and transition: all never ships. reduced-motion: every motion-bearing selector is named again
    inside a prefers-reduced-motion: reduce guard.

  • Suppression is a co-located, reasoned, counted comment:
    cairn-audit-disable-next-line <rule-id> -- <reason>, in HTML, //, or /* */ form. The reason
    is required, a directive that silences nothing is itself a finding, and neither of those errors
    can be suppressed in turn, so a build that passes by suppression reads as one. "Next line"
    resolves to the next AST node rather than the next physical line, which is what makes a directive
    above a multi-line element attach to that element's whole source range. This replaces the
    file-plus-token JSON allowlists the repo's own gates carried, whose entries orphaned silently on
    a rename and exempted a whole file at a time.

  • Two repo gates graduate onto the packaged engine and keep their names and their places in CI.
    npm run check:invisible-craft now runs motion-band, gap-scale, and token-colors, and
    npm run check:admin-css-classes runs no-uncompiled-class; both are thin wrappers, and the
    hand-rolled comment strippers, brace-matched attribute scanners, and duration and color regexes
    they carried are gone. Their two JSON side files go with them: every entry in
    admin-css-classes-allowlist.json proved already dead, and each entry in
    invisible-craft-budget.json resolved to a regex false positive the new rules recognize as
    compliant, a site outside the audited scope, a declared palette file, or a co-located suppression
    directive at the site itself.

  • The package now ships a norms manifest: the admin's measured design norms as data, derived by
    rendering the admin screens in both themes and reading the computed styles of twelve semantic
    roles. npx cairn-audit norms <selector-or-role> answers from it, so a developer or an agent
    building a new admin surface reads a measured control height, padding ratio, border treatment,
    radius, or type recipe instead of inferring one from a screenshot. Each entry states the band, how
    many distinct element sites it rests on, whether a ratified decision settles it, and every caveat:
    an entry an open design question governs is flagged and never reads as settled, a band resting on
    one site says so, and a palette-dependent property is stored as a relationship
    (var(--cairn-card-border), a color-mix formula) rather than a resolved Warm Stone value, so a
    re-tuned palette invalidates nothing. See The cairn-audit CLI.

  • cairn-audit's rendered mode has a working harness. It renders every configured admin page in
    both themes, always, since a rendered rule can pass one theme and fail the other; it never starts
    a server (BASE_URL must already answer, or the run fails naming the URL it tried), and it imports
    Playwright dynamically from the consumer's own install, printing a one-line
    npm i -D playwright && npx playwright install chromium instruction when it is absent. A rule
    declares which interaction states it reads from (a rest render, an open menu, a keyboard
    focus-visible pass), so a rule that never needs a menu-open pass never pays for one. Exemptions
    live in a page+selector+reason JSON allowlist (rendered.allowlist in
    cairn-audit.config.json), the same reason-required discipline the static suppression comments
    carry; an allowlist entry whose selector matches nothing the run actually visited is reported as
    a stale entry rather than silently doing nothing, the same fail-loud discipline every other part
    of the engine holds. See The cairn-audit CLI.

  • `npx cairn-aud...

Read more

v0.90.1: ListToolbar select sizing and menu-facet disclosure/a11y fixes

Choose a tag to compare

@glw907 glw907 released this 25 Jul 04:35

Three fixes inside ListToolbar, all surfaced by a fresh-context coherence read of a consumer's Members screen. No consumer action is required.

Fixed

  • ListToolbar's 'select' facet no longer pins to daisyUI's own fixed 320px clamp (width: clamp(3rem, 20rem, 100%), whose middle value is a fixed length, not a container-relative one); it now sizes to its own content (width: auto; max-width: 100%), so a short-option facet renders narrow and a container of real-world facets (four selects plus a menu facet plus search) fits one line at the widths the Members-refinement recomposition targeted. A select facet's border also now shares the 'menu' facet's own var(--cairn-card-border) treatment (via the same --input-color custom property daisy's compiled rule already reads), so the two read as one control family rather than two vocabularies side by side.
  • A 'menu' facet's option list and the overflow disclosure's own panel no longer open on keyboard focus alone: daisyUI's .dropdown shows .dropdown-content on :focus-within for free, which let Tabbing onto a trigger reveal the panel while aria-expanded (driven purely by this component's own dropdown-open class) stayed false. Both disclosures' visibility now tracks dropdown-open exactly, so aria-expanded always matches what is actually shown.
  • A 'menu' facet's option list carries real ARIA menu semantics (role="menu" with role="menuitemradio" options, previously bare buttons in a plain <ul>): the applied value is exposed programmatically via aria-checked, not just the sighted-only check glyph, and a roving tabindex makes only the focused option a Tab stop, with ArrowUp/ArrowDown/Home/End moving that focus, wrapping at the ends, mirroring the segmented filter's own keyboard model. Escape-closes-and-returns-focus and the click-to-select behavior are unchanged.

v0.90.0: ExpandableRow graduation, a ListToolbar menu facet, formatPhone

Choose a tag to compare

@glw907 glw907 released this 24 Jul 03:56

0.90.0

Added

  • ExpandableRow graduates into the admin-toolkit subpath (second consumer landed:
    aksailingclub-org's own src/admin-club/toolkit/ExpandableRow.svelte), carrying three visual
    fixes from the Members-refinement audit: the summary row's hover wash now reaches the sticky
    trigger cell with its own opaque tint (a transparent-based wash would let content scrolling
    underneath a pinned column show through); the trigger cell follows zebra parity
    (base-200 on striped rows) instead of a hardcoded base-100, closing the right-edge seam;
    and the expanded panel's <td> gets background: var(--color-base-300) with an inset
    top-border, so the drawer reads as its own surface instead of merging with the zebra stripe.
    All of the component's contracts (colspan mechanics, the 390 column-hiding pattern,
    aria-expanded/keyboard behavior) are unchanged.
  • ListToolbar gains a display: 'menu' filter variant: a quiet bordered button showing the
    facet's name at rest ("Standing") and its applied value in-control ("Standing: Overdue") with
    a separate inline clear affordance, opening a keyboard-operable option list (focus moves to
    the first option on open, Escape and outside-pointerdown close it, only one facet stays open
    at a time). The existing 'select' variant is unchanged behaviorally and now shares the same
    30px control height and 13px text as every other control in the row.
  • formatPhone(phone: string): string joins the admin-toolkit formatters: a stored E.164 NANP
    number (+19075550100) renders as the hyphenated 907-555-0100 for a table cell; anything
    outside that shape passes through unchanged.

Changed

  • ListToolbar's controls row recomposes from a rigid repeat(auto-fill, minmax(11rem, 1fr))
    grid to a wrapped flex row (search flex: 1 1 240px), so five promoted facets plus search fit
    one line at a realistic container width and wrap cleanly at narrow widths instead of forcing
    ragged per-cell wrapping. The applied-pills row is retired; computeAppliedFilters now only
    feeds the count line. The 13px count line gains tabular-nums, as does Pagination's range
    line.
  • StatusChip's border demotes from badge-outline's full-strength currentColor (which read
    as a clickable button) to color-mix(in oklab, currentColor 35%, transparent), the value
    verified against zebra stripes in both themes; the sm size keeps its 5rem min-width floor.
  • OfficeList's header stack (eyebrow/title/subtitle) no longer leaks the browser's default
    h1/p margins past its own gap-0.5 (flex does not collapse child margins), so the
    rendered gaps settle to the intended few px instead of a ~32px leak; the header's action slot
    no longer stretches full-width below the sm breakpoint.
  • cairn's own ConceptList create-button label now resolves its singular noun through the same
    itemNoun grammar Pagination and ListToolbar use, instead of a bespoke fallback, so a
    concept that only declares a plural label (the showcase's pages, before this change) no
    longer reads "New Pages" where "New Page" was intended.

No consumer action is required. ExpandableRow and formatPhone are new, additive exports;
ListToolbar's 'menu' display value widens an existing string union; every other change is a
visual refinement inside cairn's own admin-toolkit and built-in admin screens.

v0.89.1: itemNoun grammatical number on toolkit count lines

Choose a tag to compare

@glw907 glw907 released this 21 Jul 09:45

Patch release: grammatical number on the admin-toolkit's count and range lines.

Added

  • itemNoun(count, label) and ItemLabel ({ one: string; many: string }) join the
    admin-toolkit subpath's formatters, and Pagination's and ListToolbar's itemLabel prop
    widens to string | ItemLabel. An { one, many } pair picks the grammatical number on every
    count and range line ("1 class", "6 classes"), graduating the fix aksailingclub-org's own
    toolkit proved for the "1 households" defect. A plain-string itemLabel renders exactly as
    before.

No consumer action is required.

v0.89.0: the admin toolkit — a public subpath, and cairn's admin re-expressed on it

Choose a tag to compare

@glw907 glw907 released this 21 Jul 08:37

The admin-toolkit organization pass: a new public subpath, and cairn's own admin re-expressed on it.

Added

  • A new public subpath, @glw907/cairn-cms/admin-toolkit, packages the general-purpose admin
    components and formatters a consumer site's own admin build proved first: PageHeader,
    ListToolbar, AdminTable, StatusChip, Pagination, EmptyState, and the
    formatMoney/formatCivilDate/formatTimestamp/ageFromBirthdate formatters. A site building
    its own /admin/ screen reaches for the same shared vocabulary cairn's own admin now uses,
    instead of hand-rolling a bespoke parallel. See the admin-toolkit reference page
    (docs/reference/admin-toolkit.md).

Changed

  • cairn's own admin screens now build on that toolkit. ConceptList, CairnMediaLibrary,
    ManageEditors, VocabularyAdmin, CairnTidySettings, NavTree, and HelpHome all render
    their page header through the toolkit's PageHeader, converging five ad hoc header markups
    into one visible idiom: an optional eyebrow, the display-face title, an optional muted meta
    line, and one right-aligned action. ConceptList and CairnMediaLibrary also converge their
    search, filter, count, table, and pager markup onto ListToolbar, AdminTable, StatusChip,
    and Pagination. A site that already matched the old per-screen headers and lists by eye may
    notice the rhythm settle to one shape; no prop or route contract changed.

Fixed

  • The admin CSS build's @source scan never included src/lib/admin-toolkit, so any daisyUI or
    Tailwind class used only there silently never compiled. A new CI gate,
    check:admin-css-classes, checks every admin component template's class tokens against the
    built sheet so a class miss like this fails CI instead of shipping.

No consumer action is required. The new subpath is additive, and the visible header convergence
touches only cairn's built-in admin screens, with no exported contract behind it.

v0.88.3: a blessed daisyUI safelist for the admin, plus the banked showcase window

Choose a tag to compare

@glw907 glw907 released this 20 Jul 20:44

0.88.3

Changed

  • A new documented safelist source, src/lib/components/admin-css-safelist.ts, makes the admin CSS
    build compile a curated blessed set of daisyUI 5 classes that no shipped admin component references
    yet: the stats/stat-* family, table-zebra/table-xs, toast plus its placement modifiers,
    the indicator/status/join placement and orientation modifiers, and badge-soft/
    badge-outline/badge-dash. The pass-B "admin CSS class-inventory gap" harvest finding named the
    trap (a daisy class only styles anything once cairn's build compiles it); the ASC admin-toolkit
    design survey defines this blessed set, so a site-authored admin screen can use the vocabulary ahead
    of any cairn component adopting it. No consumer action is required: this is an internal build change
    no consumer imports.
  • The showcase's banked IntroLedger syncs to cairn.pub's ratified masthead form: the title
    at the full step-5 masthead grade with the scaled mark, the answer one grade up, the summary
    as full-ink paragraphs (summary is now string[]), and the leadIn prop replaced by
    ledgerTitle, a tracked small-caps label on a running hairline.
  • A new showcase theme component, Carousel.svelte, banks cairn.pub's screenshot carousel in
    generic form: stacked cross-fading slides with a caller-supplied label row, one 7-second
    auto-advance cycle that then rests, pause on hover and focus, permanent manual control on any
    dot click, and no auto-advance or transition under reduced motion.
  • The showcase's content component set banks a new micro-cta, the cta's compact
    further-reading sibling: the same label-plus-link shape without the primary/secondary
    variant choice, plus an optional note line for a short gloss on where the link goes. The
    styleguide's reading surface demonstrates it beside the full cta.

No consumer action is required for the three showcase template components either; existing sites
keep their own copies until they choose to pull the updated forms.

v0.88.2: the template's nav wiring and the arm-index gate

Choose a tag to compare

@glw907 glw907 released this 19 Jul 22:18

The chassis-nav window: the template's nav wiring and a docs coverage gate, the pre-Topo pass.

Changed

  • The showcase's public header nav now renders from site.config.yaml's menus.primary
    (resolved through the engine's extractMenu by the root layout server load) instead of a
    hardcoded array, so an editor's /admin/nav changes reach the rendered site. A lean
    src/theme/site-config.ts module now owns the template's one parseSiteConfig call, and
    cairn.config.ts re-exports siteConfig from it, keeping the full adapter out of the client
    bundle. The footer's nav stays hardcoded on purpose; its list is different content, not a copy
    of the primary menu.
  • The tutorial's nav milestone teaches the same shape: parse the site config in a lean module,
    read the menu in a +layout.server.ts, and render from data.nav, instead of importing the
    config module in a client script. The theme tutorial (build-a-theme.md) is now linked from
    the docs front door.
  • A new repo gate, check:arm-indexes, fails CI when a published docs page is missing from its
    arm's index (the inverse of check:docs's dead-link direction).

No consumer action is required. The template changes land in newly scaffolded or copied sites;
an existing site keeps its own chrome.

v0.88.1: dev-backend fixtures, mermaid passthrough

Choose a tag to compare

@glw907 glw907 released this 19 Jul 20:44

The dev-backend pass. Fixes the two local-development blindspots and one renderer gap:

  • A mermaid fence now passes through the build-time Shiki highlighter untouched, keeping its language-mermaid class on the <code> element. A site's client-side mermaid renderer can key on the class directly; the site-side marker-plugin workaround is no longer needed.
  • @glw907/cairn-cms-dev seeds two published fragments (manifest rows plus bodies, one mirroring the showcase's trail-safety-notice), so the fragment picker, include-chip title resolution, and preview splice all work under vite dev.
  • @glw907/cairn-cms-dev's seeded media objects are real, browser-decodable 240x160 PNGs (a distinct solid color per asset) instead of a 12-byte stub, so the Media Library shows real thumbnails locally instead of "Image missing" on every tile.

Also in this window: a getPlatformProxy media-delivery smoke so the miniflare RPC-serialization class fails in CI rather than on a consumer's first vite dev; the check:custom-surface and check:chassis-boundary gates wired into CI; the editor's media chip no longer decorates a media: token inside a fenced code block, and figureRoleAtLine reads the anchored attrs group; theme-import and preview-iframe documentation.

Consumers must: nothing. All changes are additive, dev-package-internal, or non-breaking behavior fixes.

v0.88.0: the admin access map, collapse defaults, icon overrides, and attention badges

Choose a tag to compare

@glw907 glw907 released this 19 Jul 12:52

0.88.0

Added

  • The admin access map: a site declares defineAccess(roles, map) once, mapping engine screens
    and its own /admin routes to the role names admitted to each, and one function, canReach,
    is the single authority the guard's new requireAccess helper, every engine route gate, and
    the sidebar resolver all read, so route enforcement and nav visibility can never drift apart.
    Capability stays the floor (owner always passes, none never reaches anything, editors
    keeps its owner-only floor); the map only narrows an editor-capability session further. A site
    that declares no map sees no behavior change. See Restrict admin access by
    role
    . New log event auth.access.denied (warn:
    email, role, target) makes a map denial observable.
  • NavLayoutSection gains collapsed?: boolean, the group's declared starting state for a
    visitor with no persisted nav-collapse cookie (default false, today's behavior); the
    existing cookie still wins entirely once any header is touched.
  • NavLayoutEngineRef gains icon?: AdminNavIcon, overriding the engine-owned glyph for that
    door, and the bundled AdminNavIcon/ADMIN_NAV_ICON_NAMES allowlist widens from nine names to
    twenty-seven (the full working set surfaced by ASC's own declared sidebar).
  • A new attention dependency (ContentRoutesDeps.attention, CairnAdminDeps.attention),
    awaited once per request and never cached: a site returns AttentionItem[] (href, count,
    an optional label) and the shell renders a quiet pending-work pill on the matching visible
    nav entry, summed on a collapsed section's header, dropped at zero, and dropped entirely for
    an item whose href the current session cannot see (a count never leaks to a role that can't
    act on it).

No Consumers must: action: every addition above is additive, and a site that declares none of
it sees no behavior change.