@tribulnation/ui
v0.5.0
Published
Tribulnation Labs shared design system: tokens, hash-driven light/dark/system + brand axes, logo marks, and font loading.
Readme
@tribulnation/ui
Shared design system for Tribulnation Labs sites: CSS tokens for the light/dark/system + brand (logo/palette/font) axes, the hash-driven state engine behind them, the mode switch, logo marks, and font loading.
First integrated into tribulnation/landing. Try it live via the interactive showcase at tribulnation.github.io/design/showcase, or grab static logo/lockup files from the brandkit.
The mark SVGs (src/lib/marks/*.svg) aren't hand-edited here — they're
synced from /assets/marks (this repo's root, one level up) by
scripts/sync-assets.mjs, which runs automatically as part of npm run
package. If you're working on this package directly, run
npm run sync-assets once after cloning (or after /assets/marks changes)
so svelte-check/local dev have the files to import.
Install
npm install @tribulnation/uiUsage
/* app.css or similar, imported once */
@import '@tribulnation/ui/tokens.css';
@import '@tribulnation/ui/marks.css';<script lang="ts">
import { onMount } from 'svelte'
import {
watchAxes,
setAxis,
setPersistedAxis,
prefersColorSchemeResolver,
ModeSwitch,
FontLinks,
Logo,
type Axes
} from '@tribulnation/ui'
const config = {
defaults: { logo: 'brush', palette: 'coral', mode: 'system', font: 'marker', 'logo-outline': 'none' },
validValues: {
logo: ['geo', 'brush', 'signal'],
palette: ['coral', 'lime'],
mode: ['dark', 'light', 'system'],
font: ['system', 'inter', 'orbitron', 'audiowide', 'marker', 'sriracha'],
'logo-outline': ['none', 'circle', 'square']
},
// mode remembers the user's last click in localStorage instead of the
// hash — everyday toggling shouldn't rewrite the address bar. The hash
// still overrides whenever present, e.g. for a QA/share link.
persist: ['mode']
}
let axes: Axes = $state({ ...config.defaults })
onMount(() => {
const system = prefersColorSchemeResolver()
return watchAxes({ ...config, system }, (resolved) => (axes = resolved))
})
</script>
<FontLinks />
<Logo class="brand-logo" />
<ModeSwitch value={axes.mode as any} onchange={(v) => setPersistedAxis('mode', v)} />Every axis is overridable via the URL hash for QA (e.g.
#logo=geo&palette=lime&font=orbitron) — that always takes priority. Axes
not listed in persist are written back to the hash by setAxis when
changed (so the showcase's "Copy link" stays shareable); axes listed in
persist (typically just mode) are written to localStorage by
setPersistedAxis instead, so routine toggling doesn't touch the URL.
Brandkit
The full brandkit — mark, maskable icon, both lockups, and social cover
images, each as SVG + PNG@512/2048, plus a "download everything" zip — ships
under dist/brandkit/, generated at package-build time from the same
/assets source as the GitHub Pages brandkit (scripts/build-brandkit.mjs
invokes the repo-root scripts/build-brand.mjs with a redirected output
dir). Every file is importable via the ./brandkit/* export, e.g.:
import markBg from '@tribulnation/ui/brandkit/mark/background.svg'
import zipUrl from '@tribulnation/ui/brandkit/tribulnation-brandkit.zip?url'Consumers get Vite's normal asset pipeline for free — bump the package
version, npm install, rebuild, done. No sync script, no committed copies.
What's in here, and what isn't
Included: design tokens, the axis-sync engine, the mode switch, logo marks, font loading, and the brandkit — the parts that are genuinely the same shape across sites.
Deliberately left out: page layout, nav/footer chrome, buttons, cards — those vary per site and would just bake one site's copy/structure into a "shared" package prematurely. Extract further only once a second real consumer shows what's actually reusable there.
Publishing
Bump version in packages/ui/package.json and push to main.
.github/workflows/publish-ui.yml builds the package and publishes to npm
whenever that version isn't already on the registry — no tagging step
needed.
Publishing uses npm's Trusted Publishing
(OIDC): the workflow requests a short-lived identity token from GitHub
(id-token: write), and npm verifies it against the Trusted Publisher
configured on the package (GitHub Actions, repo tribulnation/design,
workflow publish-ui.yml, no environment) — no NPM_TOKEN secret to store
or rotate.
