Skip to the content
Krizaka TypeScript
Krizaka TypeScript

@krizaka/config

The quality kit of every TypeScript repository: ESLint configs, the dependency-cruiser preset, tsconfig bases, Prettier and the debt ratchet.

Install

bashTerminal
npm install @krizaka/config

Also published as subpath exports: @krizaka/config/eslint, @krizaka/config/eslint/next, @krizaka/config/eslint/krizaka-ui, @krizaka/config/eslint/no-locale-ternary, @krizaka/config/eslint/stack, @krizaka/config/eslint/ui-warnings, @krizaka/config/eslint/env-names, @krizaka/config/eslint/plugin, @krizaka/config/dependency-cruiser, @krizaka/config/covered-domains, @krizaka/config/covered-domains.json, @krizaka/config/known-violations, @krizaka/config/patterns, @krizaka/config/ratchet, @krizaka/config/prettier, @krizaka/config/tsconfig/base.json, @krizaka/config/tsconfig/react-library.json, @krizaka/config/tsconfig/next.json, @krizaka/config/tsconfig/react-native.json, @krizaka/config/editorconfig.

The shared configuration of every Krizaka JavaScript repository. ESLint flat configs (base, Next.js, the four UI rules, the three stack rules, no locale ternary), the dependency-cruiser boundary, tsconfig bases, Prettier, EditorConfig — and krizaka-ratchet, the counter that keeps the UI and stack debts from coming back.

npm License

Adopt it in three lines

npm install -D @krizaka/config eslint typescript     # + eslint-config-next for a Next.js app, prettier if you format
// eslint.config.mjs — a Next.js app (a library: `krizakaBase` from "@krizaka/config/eslint")
import { krizakaNext } from "@krizaka/config/eslint/next";
import { krizakaUi } from "@krizaka/config/eslint/krizaka-ui";
import { noLocaleTernary } from "@krizaka/config/eslint/no-locale-ternary";
import { krizakaStack } from "@krizaka/config/eslint/stack";

export default [...krizakaNext, ...krizakaUi(), ...krizakaStack(), ...noLocaleTernary()];
// .dependency-cruiser.cjs — « Krizaka first », cycles, unresolvable imports, dev dependencies, plus the app's own rules
module.exports = { extends: "@krizaka/config/dependency-cruiser", forbidden: [] };
// tsconfig.json — base · react-library · next · react-native
{ "extends": "@krizaka/config/tsconfig/next.json", "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] }

Prettier: export { default } from "@krizaka/config/prettier"; in prettier.config.js (120 columns, double quotes, trailing commas). EditorConfig: cp node_modules/@krizaka/config/.editorconfig ..

eslint ^9 and typescript ^5 are peer dependencies; eslint-config-next (Next.js apps) and prettier are optional peers. The plugins (typescript-eslint, eslint-plugin-react-hooks, eslint-plugin-simple-import-sort) come with the package.

What is inside

ExportWhat it gives
@krizaka/config/eslintkrizakaBase: @eslint/js + typescript-eslint recommended, React hooks, import ordering (a warning, autofixable), ignores of generated output, _-prefixed unused arguments allowed.
@krizaka/config/eslint/nextkrizakaNext: eslint-config-next (core web vitals + TypeScript, which already carry typescript-eslint, React hooks and import) + the same basics.
@krizaka/config/eslint/krizaka-uikrizakaUi(options?): the four UI rules below. Also krizakaUiRules and krizakaUiRestrictedSyntax(options?).
@krizaka/config/eslint/no-locale-ternarynoLocaleTernary(options?): krizaka/no-locale-ternary — no locale === "fr" ? … : … outside routing code.
@krizaka/config/eslint/ui-warningskrizakaUiWarnings(options?): the same two debts as named plugin rules, as warnings — krizaka/no-light-variant (no light:: tokens do the theming) and krizaka/no-raw-palette (no bg-zinc-900, text-violet-400: a role; allow families). krizaka-ratchet counts them (light, palette), so a product adopts them now and brings the counts to 0.
@krizaka/config/eslint/stackkrizakaStack(options?): the three stack rules below (warnings). Also STACK_RULES.
@krizaka/config/eslint/pluginkrizakaPlugin: the krizaka plugin carrying every rule, for a hand-written config.
@krizaka/config/dependency-cruiserThe shared dependency-cruiser config, generated from the covered-domains map: one krizaka-first-<domain> rule per domain (dates, i18n, query, icons, ui), no-circular, not-to-unresolvable, not-to-dev-dep. .withOptions({ exemptFrom }) for a monorepo whose own packages are the facades.
@krizaka/config/covered-domains · /covered-domains.jsonThe covered-domains map (one data file) and its helpers: the message, the patterns, the Markdown table of « Use Krizaka first ».
krizaka-known-violations (bin) · @krizaka/config/known-violationsThe known violations of dependency-cruiser, which only shrink, see below.
@krizaka/config/tsconfig/*.jsonbase (strict, ES2022, bundler resolution), react-library (+ DOM, react-jsx), next (+ the Next plugin, noEmit, incremental), react-native (jsx: react-native, customConditions; with Expo: "extends": ["expo/tsconfig.base", "@krizaka/config/tsconfig/react-native.json"]).
@krizaka/config/prettier · .editorconfigThe formatting.
krizaka-ratchet (bin)The debt counter, see below.

The four UI rules

On every string literal inside a className (in cn(…), a ternary…), with no plugin — no-restricted-syntax selectors:

  1. No raw palette colour — bg-zinc-900, text-violet-400/80: use a role of @krizaka/tailwind (bg-surface-1, text-fg-secondary, border-border-default, text-accent, text-danger).
  2. No light: — a theme is a set of tokens; a dark island takes .theme-dark on its ancestor.
  3. No arbitrary [var(--…)] utility — the variable has a utility in @krizaka/tailwind.
  4. No template literal in className — cn(…) merges the classes and lets the override win.
krizakaUi({
  allow: ["emerald"],             // palette families tolerated for documented, isolated cases
  files: ["**/*.{jsx,tsx}"],      // default
  ignores: ["app/og/**"],         // e.g. generated OG images
  severity: "error",              // "warn" while the ratchet brings the debt down
});

Flat config replaces a rule's options instead of concatenating them: a repository that already sets no-restricted-syntax merges both lists with krizakaUiRestrictedSyntax(). no-locale-ternary is a plugin rule for that reason — it composes with anything.

The stack rules

From ADR-001 — the TanStack engines run headless, behind @krizaka/* facades; a product never imports them. Warnings: krizaka-ratchet counts the same patterns, so an app adopts them before it is clean and brings each count to 0 by the end of its adoption.

  1. krizaka/no-fetch-in-client-effect — a fetch / krizakaFetch inside a useEffect (or useLayoutEffect) of a "use client" file: render the first screen in a Server Component, read the rest with @krizaka/query.
  2. krizaka/no-local-table-or-form — a <table>, or a <form> of more than one field (native fields and the field primitives of @krizaka/ui; a hidden or submit <input> is not a field): use DataTable and createKrizakaForm. A search box or a one-field newsletter stays a plain form.
krizakaStack({
  files: ["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"], // default
  ignores: ["packages/**"],                        // e.g. the facades of a monorepo
  severity: "warn",                                // default; "error" once the app's counts are at 0
  maxFormFields: 1,                                // default
});

« Krizaka first » — the refused imports (dependency-cruiser)

Part of the stack ADR and of krizaka-ui#609, in one tool: dependency-cruiser carries every refused import (an import is an edge: it catches re-exports and dynamic imports, and the same file holds the layers); ESLint keeps the framework rules above. The ESLint rule krizaka/no-direct-tanstack-import is retired: @tanstack/* is krizaka-first-query.

The covered-domains map is covered-domains.json, the single source. Each domain names its Krizaka home and what is refused in an app; the preset generates krizaka-first-<domain> from it, and the page « Use Krizaka first » of ui.krizaka.com is laid out from it (coveredDomainsMarkdown()).

DomainKrizaka homeRefused in an appRule
Dates, times, local days@krizaka/intl (formatting, localDay), @krizaka/time (arithmetic; a facade created on the first real need, krizaka-ts#76)date-fns*, @date-fns/*, dayjs, luxon, moment, moment-timezonekrizaka-first-dates
Words, languages@krizaka/i18ni18next*, react-i18next, next-intl, next-i18next, react-intl, @formatjs/*, @lingui/*, i18n-js, typesafe-i18nkrizaka-first-i18n
Data fetching, cache, tables, forms, virtual lists@krizaka/query, @krizaka/ui, @krizaka/pacer@tanstack/*krizaka-first-query
Icons@krizaka/iconslucide-react, react-icons, @heroicons/*krizaka-first-icons
Interface primitives@krizaka/ui@radix-ui/*, radix-uikrizaka-first-ui

An entry is a package (dayjs, and its subpaths), a prefix (date-fns*) or a scope (@tanstack/*). It matches the resolved file and the module as written when it cannot be resolved (a package the app does not declare), never a local folder of the same name. A Krizaka package is exempt: it is the facade. In an app that is the packages in node_modules; in a monorepo that holds its own Krizaka packages, say where they live:

// .dependency-cruiser.cjs of a monorepo (krizaka-ui, krizaka-ts)
module.exports = require("@krizaka/config/dependency-cruiser").withOptions({ exemptFrom: ["(^|/)packages/"] });

Every message names the home and ends « Missing function? add it to the package first. » — a missing function is added to the Krizaka package, never worked around with the library. Run it with depcruise --output-type err-long <folders> to read the message (err prints the rule name only). Beside the domains, the preset owes every repository no-circular, not-to-unresolvable (declare the dependency, or fix the path; set options.tsConfig for path aliases) and not-to-dev-dep (tests, stories, scripts and tool configs may import devDependencies). Run depcruise from the workspace that owns the package.json.

Known violations (they only shrink)

An app that adopts the rules late records what it already breaks, and the debt only goes down:

krizaka-known-violations init src app         # once, in the adoption pull request: writes .dependency-cruiser-known-violations.json
depcruise src app --config .dependency-cruiser.cjs --ignore-known .dependency-cruiser-known-violations.json   # lint: new violations fail
krizaka-known-violations check src app        # lint: also fails on a recorded violation that is fixed ("lock the gain")
krizaka-known-violations update src app       # removes the fixed ones; NEVER adds one, and fails while a new one exists

The file is the format of depcruise-baseline, so --ignore-known reads it. init refuses to overwrite it; there is no command that records a new violation. It replaces the directTanstackImport counter of krizaka-ratchet, which stays only for repositories that have not moved yet.

noLocaleTernary({ files, routing }): routing lists the files allowed to compare locales (default: proxy.ts, middleware.ts, i18n.ts, seo.ts, app/[locale]/layout.tsx, I18nProvider.tsx).

Environment variable names (krizaka/env-name)

Environment variables are named by bounded context (krizaka/.github#46): <CONTEXT>_<THING> with CONTEXT one of APP_ DATABASE_ AUTH_ MEDIA_ MAIL_ PUSH_ PAYMENTS_ REALTIME_ ABUSE_ OBSERVABILITY_ AI_ OPERATOR_. The rule — on in krizakaBase and krizakaNext — refuses a name read through process.env.X, process.env["X"] or import.meta.env.X that starts (after NEXT_PUBLIC_, EXPO_PUBLIC_ or VITE_) with:

  • a product: the organisation's name, the repository's own (the first word of its package.json name, acme-monorepo → ACME_), or one you name — a deployment already says which product it is;
  • a vendor (BUNNY_, MAILGUN_, RESEND_, STRIPE_, GOOGLE_, EXPO_, REDIS_…): the vendor is a value (MEDIA_STORAGE_PROVIDER=bunny, MAIL_PROVIDER=mailgun) and its settings sit under the context (MEDIA_STORAGE_API_KEY, MAIL_API_KEY).

The platforms' own names (GITHUB_TOKEN, EAS_BUILD, …) pass. Tune it with envNames({ products, vendors, allow }) from @krizaka/config/eslint/env-names; envNamePrefixProblem(name) gives the same verdict to a script (a .env.example or deployment spec check).

The ratchet

A repository with debt cannot turn the rules on in one pull request. The ratchet lets it adopt them now: it counts the four UI debts and the four stack debts, records the counts in lint-ratchet.json at the root, and fails when a count goes up. Every migration pull request lowers them; when a count reaches zero, the matching rule becomes a strict error.

npx krizaka-ratchet --init      # once: creates lint-ratchet.json with today's counts — commit it
npx krizaka-ratchet             # locally: compares (exit 1 when a count went up)
npx krizaka-ratchet --update    # after a migration: lowers the recorded counts (never raises them), records new counters — commit it
npx krizaka-ratchet --json      # in CI: the same check, machine-readable
CounterPattern (same regex as the ESLint rule)
palette\b(bg|text|border|from|via|to|ring|shadow|fill|stroke)-(zinc|slate|…|indigo)-[0-9]
light\blight:
arbitraryVar\[var\(--
classNameTemplateclassName=\{\s*`
fetchInClientEffectin a file opening with "use client": fetch( / krizakaFetch( between the parentheses of a useEffect( / useLayoutEffect(
directTanstackImportfrom / import / import( / require( of "@tanstack/…"
localTableOrForm<table, and <form … </form> holding more fields than maxFormFields (default 1)
retiredImportan import of a dependency listed in retire (e.g. ["framer-motion"]): the way out of a dependency
routeStateSelectiona Tabs / chip group / choice grid / segmented switch (incl. RadioGroup.Root variant="segmented") / toggle group of a view driven by local state (value, defaultValue, onValueChange) without routeKey, and without // @ephemeral: <reason>: the URL is the state

The UI counters scan app/**/*.tsx, components/**/*.tsx and src/**/*.tsx by default (node_modules, dist, .next never), or the globs given on the command line; the stack counters scan every source file (js, jsx, mjs, ts, tsx, mts) of app, components, src, lib, hooks and features (stackGlobs). --init stores both in the file so that every later run counts the same set. A file written before the stack counters still checks; the next --update records them at their current value. The counts apply the regex to the whole source text — a class held in a constant or a cn() argument counts too — so the ratchet is never looser than the rule. Exit codes: 0 ok, 1 a count went up, 2 usage or file error.

// lint-ratchet.json
{
  "globs": ["app/**/*.tsx", "components/**/*.tsx", "src/**/*.tsx"],
  "stackGlobs": ["app/**/*.{js,jsx,mjs,ts,tsx,mts}", "…"],
  "allow": [],                                   // optional: same families as krizakaUi({ allow })
  "retire": ["framer-motion"],                   // optional: dependencies on their way out
  "maxFormFields": 1,                            // optional: same option as krizakaStack({ maxFormFields })
  "counters": {
    "palette": 2400, "light": 1151, "arbitraryVar": 0, "classNameTemplate": 128,
    "fetchInClientEffect": 41, "directTanstackImport": 3, "localTableOrForm": 38, "retiredImport": 4
  }
}

The CI step, after the install:

- run: npx krizaka-ratchet --json

License

Apache-2.0

Exports

The 0 names the package exports, read from its types.

NameKindWhat it is

@krizaka/config/eslint

NameKindWhat it is
defaultconstant
KRIZAKA_IGNORESconstantGenerated or vendored output, never linted.
krizakaBaseconstant
krizakaCommonconstantShared by krizakaBase and krizakaNext: ignores, globals, import ordering.

@krizaka/config/eslint/next

NameKindWhat it is
defaultconstant
krizakaNextconstant

@krizaka/config/eslint/krizaka-ui

NameKindWhat it is
defaultfunctionFlat config: the four UI rules on the given files.
krizakaUifunctionFlat config: the four UI rules on the given files.
krizakaUiRestrictedSyntaxfunctionThe no-restricted-syntax entries, for a repository that already uses that rule and must merge both lists (flat config replaces a rule's options, it never c…
krizakaUiRulesconstantThe rule set, for a spread into a config object's rules.

@krizaka/config/eslint/no-locale-ternary

NameKindWhat it is
defaultfunctionFlat config: krizaka/no-locale-ternary on the given files, except routing code.
DEFAULT_ROUTING_FILESconstantRouting files that legitimately compare locales (the krizaka.com layout, a common convention).
krizakaPluginconstant
LOCALE_TERNARY_SELECTORSconstantThe selectors of no-locale-ternary, also usable in no-restricted-syntax.
noLocaleTernaryfunctionFlat config: krizaka/no-locale-ternary on the given files, except routing code.

@krizaka/config/eslint/stack

NameKindWhat it is
defaultfunctionFlat config: the stack rules on an app's sources.
krizakaStackfunctionFlat config: the stack rules on an app's sources.
STACK_RULESconstantThe rule names, as configured.

@krizaka/config/eslint/ui-warnings

NameKindWhat it is
defaultfunctionFlat config: the two named UI rules on a product's components.
krizakaUiWarningsfunctionFlat config: the two named UI rules on a product's components.
UI_WARNING_RULESconstantThe rule names, as configured.

@krizaka/config/eslint/env-names

NameKindWhat it is
defaultfunctionFlat config: krizaka/env-name on the given files.
ENV_PLATFORM_NAMESconstantNames the platforms set themselves (GitHub Actions, EAS): read, never chosen.
ENV_PRODUCT_PREFIXESconstantThe organisation's name is never a prefix; a repository's own product is found from its package.json (below) and a config may name more (products): a deplo…
ENV_VENDOR_PREFIXESconstantVendors: a vendor is a value (MEDIA_STORAGE_PROVIDER=bunny, MAIL_PROVIDER=mailgun), never a name.
envNamePrefixProblemfunctionThe prefix that makes an environment variable name refused, or null.
envNamesfunctionFlat config: krizaka/env-name on the given files.
krizakaPluginconstant
ownProductPrefixfunctionThe product a repository is: the first word of the nearest package.json's name, scope removed (acme-monorepo → ACME, @org/acme-admin → ACME); null for a…

@krizaka/config/eslint/plugin

NameKindWhat it is
defaultconstant
krizakaPluginconstant

@krizaka/config/dependency-cruiser

NameKindWhat it is
withOptionstype

@krizaka/config/covered-domains

NameKindWhat it is
CoveredDomaintype
coveredDomainsconstantThe domains, in the order of the map.
coveredDomainsMarkdownfunctionThe table of the page « Use Krizaka first » (Markdown), generated from the map.
domainMessagefunctionThe message of a domain: what is refused, the Krizaka home, and what to do when the function is missing.
domainPatternfunctionOne pattern for a whole domain.
MISSING_FUNCTIONconstantThe last thing every message says.
refusedPatternfunctionThe dependency-cruiser to.path regular expression of a refused entry.
ruleNamefunctionThe name of the dependency-cruiser rule of a domain.

@krizaka/config/covered-domains.json

NameKindWhat it is

@krizaka/config/known-violations

NameKindWhat it is
comparefunctionCompare the recorded violations with the current ones.
CONFIG_FILEconstant
currentViolationsfunctionRun dependency-cruiser without the known file and read every violation (the baseline report).
keyOffunctionThe identity of a violation, as depcruise matches it: the rule, the two ends, and the cycle's modules.
KNOWN_FILEconstant
runKnownViolationsfunctioncheck | update | init.
shrinkfunctionWhat update writes: the recorded violations that still happen.
Violationtype

@krizaka/config/patterns

NameKindWhat it is
ARBITRARY_VAR_SOURCEconstantbg-[var(--surface-1)] — the variable has a utility in
balancedArgumentsfunctionThe text between the parenthesis that opens at open and the one that closes it, skipping strings and comments.
CLASSNAME_TEMPLATE_SOURCEconstantclassName=\{…${x}\} — the ratchet's text approximation of the TemplateLiteral rule.
COLOR_UTILITIESconstantUtilities that take a colour.
countFormFieldsfunctionThe value-holding fields inside a form's source text.
EFFECT_CALL_SOURCEconstantThe opening of an effect call: useEffect(, React.useLayoutEffect(.
EFFECT_NAMES_SOURCEconstantThe names the ESLint rule matches (callee identifier or member property).
EPHEMERAL_SOURCEconstantThe justification that keeps a local selection: `//
FETCH_CALL_SOURCEconstantA fetch call: fetch(, window.fetch(, krizakaFetch( — never refetch(.
FETCH_NAMES_SOURCEconstant
FORM_FIELD_TAGSconstantThe elements that hold a value in a form: native fields and the field primitives of @krizaka/ui.
importSourcefunctionAn import of one of the given packages (or scopes ending in /): from "x", import "x", import("x"), require("x"), export … from "x".
isLocalSelectionfunctionIs this opening tag a selection driven by local state?
LIGHT_VARIANT_SOURCEconstantlight:bg-white — a theme is a set of tokens, never a variant.
LOCAL_SELECTION_PROPSconstantThe props that make a selection local: its own value, its starting value, its change handler.
PALETTE_FAMILIESconstantTailwind palette families a product never writes: a component reads a role (bg-surface-1, text-accent…).
paletteSourcefunctionbg-zinc-900, text-violet-400/80… — a raw palette step.
ROUTE_KEY_PROPconstantThe prop that binds it to the URL.
SELECTION_COMPONENTSconstantThe selection components whose choice belongs in the URL: tabs, a segmented switch, toggle groups, chip groups, choice tiles.
selectionTagsfunctionThe opening tags (&lt;Tabs.Root … >) of the selection components in a source text, each with the line above it.
TABLE_OR_FORM_SOURCEconstantA &lt;table or &lt;form element written in an app (JSX or HTML).
TANSTACK_SCOPEconstantThe headless engines that only enter through a @krizaka/* package.
USE_CLIENT_SOURCEconstantA "use client" directive heading the file (after comments).

@krizaka/config/ratchet

NameKindWhat it is
CounterNametype
counterPatternsfunction
Counterstype
COUNTERSconstantEvery counter, in report order.
countRepositoryfunctionCounts the debts under root: the UI counters in the files of globs, the stack counters in those of stackGlobs.
countSourcefunctionCounts the four UI debts in one source text.
countStackSourcefunctionCounts the stack debts in one source text: fetch calls inside the effects of a "use client" file, imports of @tanstack/*, hand-written tables and forms o…
DEFAULT_GLOBSconstant
DEFAULT_STACK_GLOBSconstantThe stack counters read every source file: data hooks and fetchers live in .ts too.
RATCHET_FILEconstant
RatchetErrorclassA usage or file error (exit code 2), as opposed to a counter going up (exit code 1).
RatchetFiletype
RatchetResulttype
runRatchetfunctionRuns the ratchet.
STACK_COUNTERSconstantThe stack counters (the three rules of krizakaStack, and the dependencies to retire), in report order.
StackCounterNametype
UI_COUNTERSconstantThe UI counters (the four rules of krizakaUi), in report order.
UiCounterNametype

@krizaka/config/prettier

NameKindWhat it is
defaultconstant

@krizaka/config/tsconfig/base.json

NameKindWhat it is

@krizaka/config/tsconfig/react-library.json

NameKindWhat it is

@krizaka/config/tsconfig/next.json

NameKindWhat it is

@krizaka/config/tsconfig/react-native.json

NameKindWhat it is

@krizaka/config/editorconfig

NameKindWhat it is

Package

Version on npm0.8.0 (npm)
LicenceApache-2.0
Node.js>=22
Dependencies@eslint/js ^9.39.0
Peer dependencieseslint ^9
Sourceconfig

Changelog

0.8.0

Minor changes

  • @krizaka/config now lives in krizaka-ts (the quality kit is not UI), with its history. Same name, same exports, same behaviour; the repository, bug tracker and homepage links change. Releases are published from krizaka-ts.

  • « Krizaka first » (krizaka-ui#609, #612): the covered-domains map (covered-domains.json, dates, i18n, query, icons, ui: each names its Krizaka home and the refused imports) and the dependency-cruiser preset generated from it, one krizaka-first-<domain> rule per domain, with no-circular, not-to-unresolvable and not-to-dev-dep. The message names the Krizaka home and says « Missing function? add it to the package first. ». .withOptions({ exemptFrom }) for a monorepo's own packages. New krizaka-known-violations (init, check, update): a known-violations baseline that only shrinks. New exports @krizaka/config/covered-domains and /known-violations; dependency-cruiser is now a dependency.

    Breaking: the ESLint rule krizaka/no-direct-tanstack-import is retired (and leaves STACK_RULES): the TanStack import is krizaka-first-query in dependency-cruiser, one tool only. krizaka-no-direct-tanstack is replaced by it.

0.7.0

Minor changes

  • #496 64ede61 Thanks @krizaka-agents! - Peer range eslint: ^9 || ^10 (and @eslint/js likewise). The ESLint suite and the lint of the package pass under both ESLint 9.39 and 10.13 (krizaka-ui#141).

0.6.0

Minor changes

  • #488 c4a6ce1 Thanks @krizaka-agents! - The URL is the state, made impossible to forget (Oussama, 2026-10-22: « changer les tabs doit changer les routes »).

    • New @krizaka/ui/router: useRouteState(key, { values, default, history }) keeps a value in a search parameter, typed from an as const list (an unknown value reads as the default, the default is never written, other parameters are kept); RouteStateProvider takes your router's adapter once (the History API is the default); pure readRouteValue / writeRouteValue.
    • routeKey (+ routeValues) on Tabs.Root, Chip.Group (single) and ChoiceGrid: they bind to the URL, no useState. Local state stays for ephemeral UI only.
    • @krizaka/config: the rule krizaka/route-state-selection (in krizakaStack(), severity of the stack, only pages and route components) refuses a Tabs / Chip.Group / ChoiceGrid / SegmentedControl / ToggleGroup driven by local state without routeKey, unless the line carries // @ephemeral: <reason>; the ratchet counts it as routeStateSelection (only goes down).

0.5.0

Minor changes

  • #486 0b1d80d Thanks @krizaka-agents! - No more family switcher: krizaka.com is the only way back between the Krizaka sites (Oussama, 2026-10-22).

    • NavBar.Family is deprecated (removed in the next minor once no site uses it).
    • SiteFooter lists the family on the hub only: family now defaults to current === "krizaka", so every other site's footer holds its own columns, the legal links and one quiet « A Krizaka project » line linking to www.krizaka.com. New products prop filters the hub's Products column (krizaka.com's focus products), replacing CSS that hid links by URL.
    • @krizaka/config: the krizaka/no-family-switcher rule (an error in krizakaStack()) refuses NavBar.Family so it does not come back.

0.4.2

Patch changes

  • #467 af54020 Thanks @krizaka-agents! - Vercel is retired: its platform variable names are no longer allowed by the env-name rule (a VERCEL_ variable is refused like any vendor name).

0.4.1

Patch changes

  • #179 e3bcc51 Thanks @oussamaABID! - The documentation moved to https://ui.krizaka.com, built from this repository (apps/site) at every change: the packages' homepage (what npm shows) and READMEs point there — @krizaka/tokens to /foundations/tokens, @krizaka/icons to /foundations/icons, @krizaka/fonts to /foundations/typography, the packages without UI to /packages/<name>.

0.4.0

Minor changes

  • #169 c08ae66 Thanks @oussamaABID! - Two named ESLint rules for the adoption: krizaka/no-light-variant (refuse Tailwind light: in products — tokens do the theming) and krizaka/no-raw-palette (refuse bg-zinc-900, text-violet-400… — use a role; allow families for isolated cases), registered on the krizaka plugin and shipped as warnings by krizakaUiWarnings() (@krizaka/config/eslint/ui-warnings). krizaka-ratchet already counts the same patterns (light, palette): a product adopts the warnings now and brings both counts to 0. krizakaUi() stays the strict set.

0.3.1

Patch changes

  • #143 d0e43fd Thanks @oussamaABID! - Package homepage points to the documentation on krizaka.com (the page the npm listing links to is the one that explains the package).

0.3.0

Minor changes

  • #129 86484f5 Thanks @oussamaABID! - krizaka/env-name: environment variables are named by bounded context (krizaka/.github#46) — <CONTEXT>_<THING>, never after a product (the organisation's, the repository's own read from its package.json, or one a config names) nor a vendor (BUNNY_, MAILGUN_, GOOGLE_, EXPO_…: a vendor is a value, MAIL_PROVIDER=mailgun). It reads process.env.X, process.env["X"] and import.meta.env.X, sees through NEXT_PUBLIC_ / EXPO_PUBLIC_ / VITE_, lets the platforms' own names through (GITHUB_TOKEN, VERCEL_URL, EAS_BUILD…), and is on in krizakaBase and krizakaNext; envNames({ products, vendors, allow }) (@krizaka/config/eslint/env-names) tunes it. @krizaka/realtime's error and docs name REALTIME_REDIS_URL, @krizaka/push's docs PUSH_ACCESS_TOKEN.

0.2.0

Minor changes

  • #109 406e62c Thanks @oussamaABID! - The stack rules of ADR-001 (TanStack under @krizaka/* facades): krizakaStack() (@krizaka/config/eslint/stack) brings three warnings — krizaka/no-fetch-in-client-effect (a fetch / krizakaFetch in a useEffect of a "use client" file), krizaka/no-direct-tanstack-import (any @tanstack/* import outside a @krizaka/* package) and krizaka/no-local-table-or-form (a <table>, or a <form> of more than one field). krizaka-ratchet counts the same patterns (fetchInClientEffect, directTanstackImport, localTableOrForm) and the imports of the dependencies listed under retire (retiredImport); a lint-ratchet.json without them still checks, and --update records them. New @krizaka/config/dependency-cruiser: the shared boundary (krizaka-no-direct-tanstack) an app extends. Every krizaka/* rule now lives in one plugin object (@krizaka/config/eslint/plugin), so the configs compose.

0.1.0

Minor changes

  • #7 8446ac9 Thanks @oussamaABID! - Initial release: the shared configuration of every Krizaka JavaScript repository — ESLint flat configs (krizakaBase, krizakaNext, the four UI rules krizakaUi(options?) with a palette allowlist, noLocaleTernary), tsconfig bases (base, react-library, next, react-native), Prettier, EditorConfig, and krizaka-ratchet, the counter that fails when the UI debt goes up (--init, --update, --json).

On this page