- Latest release
- 2026-10-04
- First commit
- 2026-09-29
Packages
Library · TypeScript · npm
The npm packages under this site, Taverla, Séance and on-record, typed as far as the compiler goes.
Small TypeScript packages with no third-party runtime dependency, published on npm, each lifted out of a project once a second one needed it. One translates, and knows at compile time what each message asks for; one says whether something worked, without throwing and without null; the others keep the chosen theme from flashing, take the throw out of localStorage and the clipboard, and share the reset, the focus ring and the compiler and linter settings every project starts from.
- TypeScript
- React
- Intl
- Vitest
- Biome
- Changesets
- GitHub Actions
const EN = defineDictionary({ greeting: 'Hello {name}' })
translate('greeting', { name: 'Ada' })
translate('greeting')
Expected 2 arguments, but got 1
translate('greeting', { nom: 'Ada' })
'nom' does not exist in type '{ name: string }'
translate('greting', { name: 'Ada' })
not assignable to '"greeting"'
A message’s arguments are read off the message itself: leave out {name} and the call does not compile. No code generation, no build plugin.
- Packages
- 11under @adrienlcp
- Latest release
- 2026-10-04to npm, with provenance
- Commits
- 64on main, read 2026-10-04
- Coverage
- 96%lines run by the tests of every package, read 2026-10-02
- Installed by
- 6 appsTaverla, on-record, Séance, Analytics, Scoreboard, This site
As the compiler reads it
Set the way an editor shows it: a wavy line is a build that refuses, a violet line is what the compiler works out on its own.
const parse = (input: string): Result<number, 'not_a_number'> => { const value = Number(input) return Number.isNaN(value) ? Result.failure('not_a_number') : Result.success(value) } const result = parse(raw) if (result.status === 'failure') return result.error 'not_a_number' result.data numberResult fits in thirty lines. A success that carries nothing has no data key at all, so nobody reads undefined off it.
export const themeStore = createThemePreferenceStore({ storageKey: 'app:theme' }) // vite.config.ts: runs before the first paint plugins: [themePreferencePlugin(themeStore)] themeStore.setPreference('dark') <html data-theme="dark"> dark theme-color: media="all" light theme-color: media="not all"A phone paints its toolbar from the theme-color tags, which follow the system, not the visitor. The theme package rewrites their media query before the first paint, so a dark choice on a light phone gets a dark toolbar too.
const locale = readRecognizedText({ key: 'app:locale', isRecognized: isLocale }) if (locale.status === 'failure') { locale.error 'unavailable' | 'unrecognized' } else { locale.data 'en' | 'fr' | null }Safari’s private window once threw on every localStorage write; safe-storage returns a Result there instead, as for a full quota or a value an older version stored.
What shipped
- The French dictionary is typed against the English one, down to each placeholder: a missing key or a {nom} written for {name} fails the build instead of showing on screen.
- Every rule it enforces is a compile error, so those tests are written as types and checked by tsc.
- browser copies to the clipboard even over plain HTTP, where the Clipboard API does not exist.
- Released with Changesets and published from GitHub Actions with npm provenance, never from a laptop.
- This site, Taverla, Séance and on-record install them from npm, down to their TypeScript and Biome configs.
The 11
Each at its version on npm, and which app installs it.
- 2026-10-03 · 11 packages released to npm, with provenanceDocumentationgithub.com/AdrienLcp/packages
@adrienlcp/theme-preference
Keeps the chosen theme from flashing, down to the phone’s toolbar.
0.3.0Shipped@adrienlcp/browser
Copies to the clipboard even over plain HTTP, where the Clipboard API does not exist.
0.1.1Shipped@adrienlcp/safe-storage
Takes the throw out of localStorage and returns a Result instead.
0.1.0Shipped
History
Straight from the git log, abridged: 8 of 64 commits, newest first.
- Darken the muted ink until it reads at WCAG AA on every surface, checked by a test
- Format a Temporal ZonedDateTime in {x:date}, in its own zone and calendar
- Check colour token contrast against WCAG from a test, through @adrienlcp/styles/contrast
- Add the documentation site, built from the packages' own sources
- Animate exits through CSS, and animate navigations with React's ViewTransition
- Add container query mixins and let sizes transition to keywords
- Hold back a package npm has never seen instead of failing the release
- Publish the i18n and result packagesFirst commit