packages/theme: add the @goauthentik/theme design-system package (#23341)

* packages/fonts: add @goauthentik/fonts package

Extract the bundled web fonts out of web/ into a dedicated
@goauthentik/fonts workspace package: the RedHat variable faces, the
PatternFly pficon icon face, and the FontAwesome Free solid face, each
with its upstream license recorded under licenses/.

web/ now pulls the faces and icons from the package's
@goauthentik/fonts/faces.css and @goauthentik/fonts/icons.css entry
points instead of the old #fonts/* import and the inlined pficon.scss
vendor sheet.

Anchor esbuild's [dir] at the monorepo root via outbase so assets pulled
from the out-of-web package no longer resolve to a "_.._" segment, which
Go's //go:embed silently drops from the embedded outpost build.

Co-Authored-By: Ken Sternberg <ken@goauthentik.io>

* packages/theme: add @goauthentik/theme design-system package

Add @goauthentik/theme, which defines authentik's design tokens (color,
typography, fonts, spacing, shape, shadow, motion, z-index) in TypeScript
and builds them into CSS via styleframe.

Token names carry a distinct separator per category so the token type is
recoverable from the name alone. That is worth the verbosity: it keeps
the DX legible and leaves room for automatic theme management and future
tooling built on top of the token set.

Co-Authored-By: Ken Sternberg <ken@goauthentik.io>

* web/theme: add a demo page for the theme tokens

Add a self-contained demo page, built by build-demo.mjs, that renders
every theme token — colors, typography, spacing, shape, shadow, motion —
so the design system can be eyeballed in isolation while it evolves.

Co-Authored-By: Ken Sternberg <ken@goauthentik.io>

* Heal lockfile.

* Fix spelling.

* Fix grouping.

---------

Co-authored-by: Ken Sternberg <ken@goauthentik.io>
This commit is contained in:
Teffen Ellis
2026-08-15 00:16:34 +01:00
committed by GitHub
parent 30b2c129b6
commit 189a2804c2
32 changed files with 5139 additions and 2078 deletions

6
packages/theme/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
README.md
node_modules
.wireit/
_media
!.github/README.md
public/

View File

@@ -0,0 +1,13 @@
# Prettier Ignorefile
## Node
node_modules
## Static Files
**/LICENSE
./README.md
## Build asset directories
coverage
dist
out

View File

@@ -0,0 +1,18 @@
The MIT License (MIT)
Copyright (c) 2026 Authentik Security, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,57 @@
/*
* @file Build the theme demo
*/
import { copyFileSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
import { createRequire } from "node:module";
import { basename, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const require = createRequire(import.meta.url);
const DEMO_SRC_NAME = "demo";
const DEMO_DEST_NAME = "public";
const HERE = dirname(fileURLToPath(import.meta.url));
const OUT = join(HERE, DEMO_DEST_NAME);
const FONTS_CSS = require.resolve("@goauthentik/fonts/faces.css");
const ICONS_CSS = require.resolve("@goauthentik/fonts/icons.css");
const TOKENS_CSS = join(HERE, "./dist/index.css");
const DEMO_SRC_PATH = join(HERE, DEMO_SRC_NAME);
const DEMO_SRCS = readdirSync(DEMO_SRC_PATH)
.filter((filename) => /\.(html|css|js)$/.test(filename))
.map((filename) => join(DEMO_SRC_PATH, filename));
/* It's a coincidence that the icons and font files are in the same folder. Don't rely on this in
* the future.
*/
const FONT_PATH = dirname(FONTS_CSS);
const CSS_URL_RE = /url\("(\.\/[^"]+)"\)/g;
function findFontFiles() {
let fontFiles = new Set();
for (const sheet of [FONTS_CSS, ICONS_CSS]) {
const css = readFileSync(sheet, "utf-8");
const fontpaths = Array.from(css.matchAll(CSS_URL_RE));
for (const [, path] of fontpaths) {
fontFiles.add(path);
}
}
if (!fontFiles.size) {
throw new Error("Could not find font files. Did you move them?");
}
return Array.from(fontFiles);
}
const fontFiles = findFontFiles();
mkdirSync(OUT, { recursive: true });
for (const fontFile of fontFiles) {
const target = join(OUT, fontFile);
mkdirSync(dirname(target), { recursive: true });
copyFileSync(join(FONT_PATH, fontFile), target);
}
for (const source of [TOKENS_CSS, ICONS_CSS, FONTS_CSS, ...DEMO_SRCS]) {
copyFileSync(source, join(OUT, basename(source)));
}

192
packages/theme/build.mjs Normal file
View File

@@ -0,0 +1,192 @@
import console from "node:console";
import { writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { build } from "./dist/node.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const PACKAGE_ROOT = resolve(__dirname, ".");
const OUT_DIR = resolve(PACKAGE_ROOT, "dist");
const HEADER = `/*
* ⚠️ GENERATED FILE — do not edit directly.
*
* Source: packages/theme/src/tokens/*.ts
* Builder: packages/theme/build.mjs
*/
`;
/**
* @typedef {object} Category
* @property {string} name Slug used for the output filename.
* @property {string[]} prefixes
* Token-name prefixes (after the `--ak-` strip) that belong to this category.
*/
/** @type {Category[]} */
const CATEGORIES = [
{ name: "color", prefixes: ["global--color--"] },
{
name: "typography",
prefixes: [
"global--font-family--",
"global--font-size--",
"global--font-weight--",
"global--line-height--",
],
},
{ name: "spacing", prefixes: ["global--space--"] },
{ name: "shape", prefixes: ["global--radius--", "global--border-width--"] },
{ name: "shadow", prefixes: ["global--shadow--"] },
{ name: "motion", prefixes: ["global--duration--", "global--easing--"] },
{ name: "z-index", prefixes: ["global--z-index--"] },
];
/**
* One emitted block within the styleframe CSS output. `header` is the line
* that opens the block (`:root {`, `@media (...) {`, `html[data-theme="..."] {`).
*
* @typedef {object} ParsedBlock
* @property {string} header
* @property {string[]} declarations
*/
/**
* Split the styleframe CSS output into top-level blocks. Each block is one of:
*
* :root { … }
* html[data-theme="…"] { … }
* @media (…) { :root { … } }
*
* Nested `:root` inside `@media` is preserved as part of the block — the
* inner declarations are kept as a flat list and re-wrapped on emit.
*
* @param {string} css
* @returns {ParsedBlock[]}
*/
function parseBlocks(css) {
/** @type {ParsedBlock[]} */
const blocks = [];
// Top-level blocks separated by blank lines in styleframe's output. Each
// block ends with a balanced closing brace at column zero. We don't need a
// real CSS parser — the styleframe emitter is regular enough that a small
// line-based state machine handles it.
const lines = css.split("\n");
let i = 0;
while (i < lines.length) {
while (i < lines.length && (lines[i] ?? "").trim() === "") i++;
if (i >= lines.length) break;
const opener = lines[i] ?? "";
const header = opener.trim();
if (!header.endsWith("{")) {
i++;
continue;
}
const openerIndent = opener.match(/^\s*/)?.[0] ?? "";
i++;
/** @type {string[]} */
const innerLines = [];
while (i < lines.length) {
const line = lines[i] ?? "";
const trimmed = line.trim();
const indent = line.match(/^\s*/)?.[0] ?? "";
if (trimmed === "}" && indent === openerIndent) {
i++;
break;
}
innerLines.push(line);
i++;
}
blocks.push({ header, declarations: innerLines });
}
return blocks;
}
/**
* Extract every `--ak-*: …;` declaration from a flat list of lines (which may
* include a nested `:root { … }` wrapper from an `@media` block).
*
* @param {string[]} lines
* @returns {string[]}
*/
function flattenDeclarations(lines) {
return lines.filter((line) => /^\s*--ak-[a-z0-9-]+\s*:/.test(line));
}
/**
* Build the CSS for one category by filtering each parsed block to that
* category's declarations and re-wrapping them. Returns null when no token in
* the tree matches the category.
*
* @param {Category} category
* @param {ParsedBlock[]} blocks
* @returns {string | null}
*/
function buildCategoryFile(category, blocks) {
/** @param {string} line */
const matches = (line) => {
const declaration = line.match(/--ak-([a-z0-9-]+):/);
if (!declaration || !declaration[1]) return false;
const name = declaration[1];
return category.prefixes.some((prefix) => name.startsWith(prefix));
};
/** @type {string[]} */
const sections = [];
for (const block of blocks) {
if (block.header.startsWith("@media")) {
// Drill into the inner `:root { … }` wrapper.
const inner = flattenDeclarations(block.declarations).filter(matches);
if (inner.length === 0) continue;
sections.push(
`${block.header}\n\t:root {\n${inner
.map((line) => "\t\t" + line.trim())
.join("\n")}\n\t}\n}`,
);
continue;
}
const filtered = block.declarations.filter(matches);
if (filtered.length === 0) continue;
sections.push(
`${block.header}\n${filtered.map((line) => "\t" + line.trim()).join("\n")}\n}`,
);
}
if (sections.length === 0) return null;
return HEADER + sections.join("\n\n") + "\n";
}
const { css } = await build();
// index.css: every token in one file, resolving no `@import`. The typefaces the
// font-family tokens name live in @goauthentik/fonts — consumers load those
// faces themselves, so this package ships no font bytes and does not rebuild
// when they change.
await writeFile(resolve(OUT_DIR, "index.css"), `${HEADER}\n${css}\n`, "utf-8");
// Per-category files: lean slices for consumers that want to cherry-pick —
// e.g. the docs site pulling color + typography on their own.
const blocks = parseBlocks(css);
const emitted = [];
for (const category of CATEGORIES) {
const content = buildCategoryFile(category, blocks);
if (content === null) {
console.warn(`⚠️ No declarations found for category ${category.name}; skipping.`);
continue;
}
await writeFile(resolve(OUT_DIR, `${category.name}.css`), content, "utf-8");
emitted.push(category.name);
}
console.log(
`✅ Wrote dist/index.css (self-contained) + ${emitted.length} category files (${emitted.join(", ")})`,
);

View File

@@ -0,0 +1,684 @@
:root {
--demo-page-max-width: 78rem;
--demo-col-min: 14rem;
--demo-hairline: color-mix(in oklab, var(--ak-global--color--border) 60%, transparent);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
scroll-padding-top: 6.5rem;
}
body {
margin: 0;
background: var(--ak-global--color--surface--muted);
color: var(--ak-global--color--text);
font-family: var(--ak-global--font-family--body);
font-size: var(--ak-global--font-size--md);
font-weight: var(--ak-global--font-weight--normal);
line-height: var(--ak-global--line-height--md);
-webkit-font-smoothing: antialiased;
}
h1,
h2,
h3 {
font-family: var(--ak-global--font-family--heading);
font-weight: var(--ak-global--font-weight--bold);
line-height: var(--ak-global--line-height--sm);
margin: 0;
}
code,
kbd,
.token-name {
font-family: var(--ak-global--font-family--code);
font-size: var(--ak-global--font-size--xs);
}
a {
color: var(--ak-global--color--link);
}
a:hover {
color: var(--ak-global--color--link--hover);
}
a:visited {
color: var(--ak-global--color--link--visited);
}
:focus-visible {
outline: var(--ak-global--border-width--md) solid var(--ak-global--color--primary);
outline-offset: var(--ak-global--border-width--md);
}
/* #region Masthead */
.masthead {
position: sticky;
top: 0;
z-index: var(--ak-global--z-index--lg);
background: var(--ak-global--color--surface);
border-bottom: var(--ak-global--border-width--sm) solid var(--ak-global--color--border);
box-shadow: var(--ak-global--shadow-sm);
}
.masthead__inner {
max-width: var(--demo-page-max);
margin: 0 auto;
padding: var(--ak-global--space--sm) var(--ak-global--space--lg);
display: flex;
flex-wrap: wrap;
gap: var(--ak-global--space--md);
align-items: baseline;
}
.masthead h1 {
font-size: var(--ak-global--font-size--xl);
letter-spacing: -0.01em;
}
.masthead h1 .accent {
color: var(--ak-global--color--accent);
}
.masthead__meta {
color: var(--ak-global--color--text--muted);
font-size: var(--ak-global--font-size--xs);
font-family: var(--ak-global--font-family--code);
}
.masthead__controls {
margin-left: auto;
display: flex;
gap: var(--ak-global--space--sm);
align-items: center;
}
.sitenav {
max-width: var(--demo-page-max);
margin: 0 auto;
padding: 0 var(--ak-global--space--lg) var(--ak-global--space--sm);
display: flex;
flex-wrap: wrap;
gap: var(--ak-global--space--xs) var(--ak-global--space--md);
font-size: var(--ak-global--font-size--sm);
}
.sitenav a {
text-decoration: none;
color: var(--ak-global--color--text--muted);
border-bottom: var(--ak-global--border-width--md) solid transparent;
transition: color var(--ak-global--duration--normal) var(--ak-global--easing--standard);
}
.sitenav a:hover {
color: var(--ak-global--color--link);
border-bottom-color: var(--ak-global--color--accent);
}
/* #endregion */
/* #region Segmented theme control */
.segmented {
display: inline-flex;
border: var(--ak-global--border-width--sm) solid var(--ak-global--color--border);
border-radius: var(--ak-global--radius--pill);
overflow: hidden;
background: var(--ak-global--color--surface--muted);
}
.segmented button {
appearance: none;
border: 0;
background: transparent;
color: var(--ak-global--color--text--muted);
font-family: var(--ak-global--font-family--body);
font-size: var(--ak-global--font-size--xs);
padding: var(--ak-global--space--xs) var(--ak-global--space--md);
cursor: pointer;
transition:
background var(--ak-global--duration--normal) var(--ak-global--easing--standard),
color var(--ak-global--duration--normal) var(--ak-global--easing--standard);
}
.segmented button[aria-pressed="true"] {
background: var(--ak-global--color--primary);
color: var(--ak-global--color--surface);
}
.segmented button:hover:not([aria-pressed="true"]) {
background: var(--ak-global--color--surface--raised);
color: var(--ak-global--color--text);
}
/* #endregion */
/* #region Layout */
main {
max-width: var(--demo-page-max);
margin: 0 auto;
padding: var(--ak-global--space--xl) var(--ak-global--space--lg) var(--ak-global--space--4xl);
display: flex;
flex-direction: column;
gap: var(--ak-global--space--xl);
}
section {
background: var(--ak-global--color--surface);
border: var(--ak-global--border-width--sm) solid var(--ak-global--color--border);
border-radius: var(--ak-global--radius--sm);
padding: var(--ak-global--space--lg);
box-shadow: var(--ak-global--shadow-sm);
}
.section__head {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: var(--ak-global--space--sm) var(--ak-global--space--md);
padding-bottom: var(--ak-global--space--sm);
margin-bottom: var(--ak-global--space--lg);
border-bottom: var(--ak-global--border-width--sm) solid var(--demo-hairline);
}
.section__head h2 {
font-size: var(--ak-global--font-size--2xl);
}
.section__count {
font-family: var(--ak-global--font-family--code);
font-size: var(--ak-global--font-size--xs);
color: var(--ak-global--color--text--muted);
background: var(--ak-global--color--surface--muted);
border-radius: var(--ak-global--radius--pill);
padding: 0 var(--ak-global--space--sm);
}
.section__note {
flex: 1 1 100%;
color: var(--ak-global--color--text--muted);
font-size: var(--ak-global--font-size--sm);
max-width: 68ch;
margin: 0;
}
.subhead {
font-size: var(--ak-global--font-size--lg);
margin: var(--ak-global--space--xl) 0 var(--ak-global--space--md);
color: var(--ak-global--color--text);
}
.subhead:first-of-type {
margin-top: 0;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--demo-col-min), 1fr));
gap: var(--ak-global--space--md);
}
/* #endregion */
/* #region Overview / audit */
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
gap: var(--ak-global--space--md);
}
.stat {
border: var(--ak-global--border-width--sm) solid var(--demo-hairline);
border-radius: var(--ak-global--radius--sm);
padding: var(--ak-global--space--md);
background: var(--ak-global--color--surface--raised);
}
.stat__value {
font-family: var(--ak-global--font-family--heading);
font-size: var(--ak-global--font-size--3xl);
font-weight: var(--ak-global--font-weight--bold);
line-height: var(--ak-global--line-height--sm);
font-variant-numeric: tabular-nums;
}
.stat__label {
font-size: var(--ak-global--font-size--xs);
color: var(--ak-global--color--text--muted);
text-transform: uppercase;
letter-spacing: 0.06em;
}
.audit {
margin-top: var(--ak-global--space--lg);
padding: var(--ak-global--space--md);
border-radius: var(--ak-global--radius--sm);
border-left: var(--ak-global--border-width--lg) solid var(--ak-global--color--success);
background: var(--ak-global--color--surface--raised);
font-size: var(--ak-global--font-size--sm);
}
.audit[data-state="stale"] {
border-left-color: var(--ak-global--color--warning);
}
.audit[data-state="error"] {
border-left-color: var(--ak-global--color--danger);
}
.audit ul {
margin: var(--ak-global--space--sm) 0 0;
padding-left: var(--ak-global--space--lg);
}
/* #endregion */
/* #region Color */
.swatch {
border: var(--ak-global--border-width--sm) solid var(--demo-hairline);
border-radius: var(--ak-global--radius--sm);
overflow: hidden;
display: flex;
flex-direction: column;
background: var(--ak-global--color--surface--raised);
}
.swatch__chip {
height: 4.5rem;
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: var(--ak-global--space--xs);
padding: var(--ak-global--space--xs);
}
/* Surface swatches are near-invisible against the card, so ring them. */
.swatch__chip--ringed {
box-shadow: inset 0 0 0 var(--ak-global--border-width--sm) var(--ak-global--color--border);
}
.swatch__body {
padding: var(--ak-global--space--sm);
border-top: var(--ak-global--border-width--sm) solid var(--demo-hairline);
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.token-name {
color: var(--ak-global--color--text);
word-break: break-all;
}
.token-value {
font-family: var(--ak-global--font-family--code);
font-size: var(--ak-global--font-size--xs);
color: var(--ak-global--color--text--muted);
word-break: break-all;
}
.badges {
display: flex;
flex-wrap: wrap;
gap: var(--ak-global--space--xs);
margin-top: var(--ak-global--space--xs);
}
.badge {
font-family: var(--ak-global--font-family--code);
font-size: var(--ak-global--font-size--xs);
line-height: 1.6;
padding: 0 var(--ak-global--space--xs);
border-radius: var(--ak-global--radius--pill);
border: var(--ak-global--border-width--sm) solid var(--ak-global--color--border);
color: var(--ak-global--color--text--muted);
white-space: nowrap;
}
/*
* Contrast verdicts. WCAG 2.1 thresholds against the token's own
* theme surface: 4.5 for body text, 3.0 for large text and for
* non-text boundaries such as borders and status fills.
*/
.badge--pass {
color: var(--ak-global--color--success);
border-color: currentcolor;
}
.badge--mid {
color: var(--ak-global--color--warning);
border-color: currentcolor;
}
.badge--fail {
color: var(--ak-global--color--danger);
border-color: currentcolor;
}
/* Marks a token the dark block does NOT redefine. */
.badge--fallthrough {
color: var(--ak-global--color--accent);
border-color: currentcolor;
}
/* #endregion */
/* #region Typography */
.specimen {
border: var(--ak-global--border-width--sm) solid var(--demo-hairline);
border-radius: var(--ak-global--radius--sm);
padding: var(--ak-global--space--md);
background: var(--ak-global--color--surface--raised);
}
.specimen__display {
font-size: var(--ak-global--font-size--4xl);
line-height: var(--ak-global--line-height--sm);
margin-bottom: var(--ak-global--space--xs);
}
.specimen__para {
font-size: var(--ak-global--font-size--sm);
margin: var(--ak-global--space--sm) 0 0;
}
.ramp {
display: flex;
flex-direction: column;
gap: var(--ak-global--space--sm);
}
.ramp__row {
display: grid;
grid-template-columns: minmax(11rem, auto) 5.5rem 1fr;
gap: var(--ak-global--space--md);
align-items: baseline;
padding-bottom: var(--ak-global--space--sm);
border-bottom: var(--ak-global--border-width--sm) solid var(--demo-hairline);
}
.ramp__row:last-child {
border-bottom: 0;
padding-bottom: 0;
}
.ramp__sample {
line-height: var(--ak-global--line-height--sm);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.weights {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: var(--ak-global--space--md);
}
.weights__col {
border: var(--ak-global--border-width--sm) solid var(--demo-hairline);
border-radius: var(--ak-global--radius--sm);
padding: var(--ak-global--space--md);
background: var(--ak-global--color--surface--raised);
}
.weights__col h4 {
margin: 0 0 var(--ak-global--space--sm);
font-family: var(--ak-global--font-family--code);
font-size: var(--ak-global--font-size--xs);
font-weight: var(--ak-global--font-weight--normal);
color: var(--ak-global--color--text--muted);
}
.weights__row {
font-size: var(--ak-global--font-size--xl);
line-height: var(--ak-global--line-height--sm);
display: flex;
justify-content: space-between;
align-items: baseline;
gap: var(--ak-global--space--sm);
}
.weights__row + .weights__row {
margin-top: var(--ak-global--space--xs);
}
.leading {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
gap: var(--ak-global--space--md);
}
/* #endregion */
/* #region Spacing */
.bars {
display: grid;
grid-template-columns: fit-content(40ch) fit-content(40ch) 1fr;
row-gap: var(--ak-global--space--sm);
column-gap: var(--ak-global--space--lg);
}
.bar {
height: var(--ak-global--space--md);
background: var(--ak-global--color--primary);
border-radius: var(--ak-global--radius--sm);
min-width: 1px;
}
/* #endregion */
/* #region Shape */
.shape-chip {
display: flex;
flex-direction: column;
gap: var(--ak-global--space--sm);
align-items: flex-start;
}
.shape-chip__box {
width: 100%;
height: 4rem;
background: var(--ak-global--color--surface--muted);
border: var(--ak-global--border-width--md) solid var(--ak-global--color--border--strong);
}
.stroke-chip__box {
width: 100%;
height: 3rem;
border-radius: var(--ak-global--radius--sm);
background: var(--ak-global--color--surface--muted);
border-style: solid;
border-color: var(--ak-global--color--border--strong);
}
/* #endregion */
/* #region Surfaces & shadow */
.surfaces {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
gap: var(--ak-global--space--md);
}
.surface-demo {
padding: var(--ak-global--space--md);
border-radius: var(--ak-global--radius--sm);
border: var(--ak-global--border-width--sm) solid var(--ak-global--color--border);
min-height: 6rem;
}
.shadow-stage {
background: var(--ak-global--color--surface--muted);
border-radius: var(--ak-global--radius--sm);
padding: var(--ak-global--space--xl) var(--ak-global--space--lg);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
gap: var(--ak-global--space--xl);
}
.shadow-card {
background: var(--ak-global--color--surface);
border-radius: var(--ak-global--radius--sm);
padding: var(--ak-global--space--md);
min-height: 5.5rem;
display: flex;
flex-direction: column;
justify-content: flex-end;
gap: 0.15rem;
}
/* #endregion */
/* #region Motion */
.motion-track {
background: var(--ak-global--color--surface--muted);
border-radius: var(--ak-global--radius--pill);
padding: var(--ak-global--space--xs);
overflow: hidden;
container-type: inline-size;
}
.motion-dot {
width: 2.5rem;
height: 2.5rem;
border-radius: var(--ak-global--radius--pill);
background: var(--ak-global--color--accent);
transform: translateX(0);
transition: transform var(--ak-global--duration--normal) var(--ak-global--easing--standard);
}
.motion-track[data-run="true"] .motion-dot {
transform: translateX(calc(100cqw - 2.5rem));
}
/* #endregion */
/* #region Z-index */
.zstack {
position: relative;
height: 11rem;
background: var(--ak-global--color--surface--muted);
border-radius: var(--ak-global--radius--sm);
overflow: hidden;
}
.zstack__layer {
position: absolute;
top: var(--ak-global--space--md);
width: 8.5rem;
height: 7rem;
border-radius: var(--ak-global--radius--sm);
border: var(--ak-global--border-width--sm) solid var(--ak-global--color--border--strong);
background: var(--ak-global--color--surface);
box-shadow: var(--ak-global--shadow-md);
padding: var(--ak-global--space--sm);
display: flex;
flex-direction: column;
gap: 0.15rem;
}
/* #endregion */
/* #region Icons */
.icons {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
gap: var(--ak-global--space--sm);
}
.icon-cell {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--ak-global--space--xs);
text-align: center;
padding: var(--ak-global--space--sm) var(--ak-global--space--xs);
border: var(--ak-global--border-width--sm) solid var(--demo-hairline);
border-radius: var(--ak-global--radius--sm);
background: var(--ak-global--color--surface--raised);
}
.icon-cell__name {
font-family: var(--ak-global--font-family--code);
font-size: var(--ak-global--font-size--xs);
color: var(--ak-global--color--text--muted);
word-break: break-word;
line-height: 1.4;
}
/*
* Demo-local glyph classes.
*
* @goauthentik/fonts ships the two @font-face rules and nothing
* else — the real .pf-icon-* / .fa classes belong to PatternFly,
* which this package deliberately does not depend on. These two
* rules are the minimum needed to prove the faces resolve.
*/
.glyph {
font-size: var(--ak-global--font-size--2xl);
font-style: normal;
line-height: 1;
color: var(--ak-global--color--text);
-webkit-font-smoothing: antialiased;
}
.glyph--pf {
font-family: "pficon";
font-weight: var(--ak-global--font-weight--normal);
}
.glyph--fa {
font-family: "Font Awesome 5 Free";
font-weight: 900; /* the only weight PatternFly 4 bundles */
}
/* #endregion */
/* #region Footer */
footer {
max-width: var(--demo-page-max);
margin: 0 auto;
padding: 0 var(--ak-global--space--lg) var(--ak-global--space--2xl);
color: var(--ak-global--color--text--muted);
font-size: var(--ak-global--font-size--sm);
}
table.provenance {
width: 100%;
border-collapse: collapse;
margin-top: var(--ak-global--space--sm);
}
table.provenance th,
table.provenance td {
text-align: left;
padding: var(--ak-global--space--xs) var(--ak-global--space--sm);
border-bottom: var(--ak-global--border-width--sm) solid var(--demo-hairline);
font-size: var(--ak-global--font-size--xs);
vertical-align: top;
}
table.provenance th {
font-family: var(--ak-global--font-family--body);
font-weight: var(--ak-global--font-weight--bold);
color: var(--ak-global--color--text);
}

856
packages/theme/demo/demo.js Normal file
View File

@@ -0,0 +1,856 @@
const CSS_URL = "./index.css";
const DARK_SELECTOR = 'html[data-theme="dark"]';
const clamp = (v) => Math.min(1, Math.max(0, v));
/* Taken from https://github.com/3fn/DesignerPunk/, but there seem to be a lot of them, all
* converging on the same magic numbers; this particular set is straight-up AI coded.
*/
function oklchToSrgb(L, C, hDeg) {
const h = (hDeg * Math.PI) / 180;
const a = C * Math.cos(h);
const b = C * Math.sin(h);
const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
const s_ = L - 0.0894841775 * a - 1.291485548 * b;
const l = l_ ** 3;
const m = m_ ** 3;
const s = s_ ** 3;
const lin = [
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,
];
return lin.map((c) => clamp(c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055));
}
function parseColor(value) {
if (!value) return null;
const v = value.trim();
const ok = v.match(
/oklch\(\s*([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+)(?:deg)?(?:\s*\/\s*([\d.]+%?))?/i,
);
if (ok) {
const num = (raw, scale) =>
raw.endsWith("%") ? (parseFloat(raw) / 100) * scale : parseFloat(raw);
return oklchToSrgb(num(ok[1], 1), num(ok[2], 0.4), parseFloat(ok[3]));
}
const hex = v.match(/^#([0-9a-f]{3,8})$/i);
if (hex) {
let h = hex[1];
if (h.length === 3 || h.length === 4) {
h = [...h].map((c) => c + c).join("");
}
return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);
}
const rgb = v.match(/rgba?\(([^)]+)\)/i);
if (rgb) {
const parts = rgb[1]
.split(/[\s,/]+/)
.filter(Boolean)
.map(parseFloat);
return parts.slice(0, 3).map((c) => clamp(c / 255));
}
return null;
}
function luminance(rgb) {
const lin = (c) => (c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
return 0.2126 * lin(rgb[0]) + 0.7152 * lin(rgb[1]) + 0.0722 * lin(rgb[2]);
}
function contrastRatio(a, b) {
if (!a || !b) return null;
const la = luminance(a);
const lb = luminance(b);
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
}
function toHex(rgb) {
if (!rgb) return "";
return (
"#" +
rgb
.map((c) =>
Math.round(c * 255)
.toString(16)
.padStart(2, "0"),
)
.join("")
);
}
// ------------------------------------------------------------------
// Parse dist/index.css.
// ------------------------------------------------------------------
/**
* Walk a stylesheet with a brace counter, yielding every innermost
* block along with the selector path that encloses it. Comments are
* stripped first so the file's banner does not attach itself to the
* :root selector.
*/
function parseStylesheet(text) {
const hexes = new Map();
for (const m of text.matchAll(/(--[\w-]+)\s*:[^;]*?\/\*\s*(#[0-9a-fA-F]{3,8})\s*\*\//g)) {
hexes.set(m[1], m[2]);
}
const stripped = text.replace(/\/\*[\s\S]*?\*\//g, "");
const blocks = [];
const stack = [];
let buf = "";
for (const ch of stripped) {
if (ch === "{") {
stack.push(buf.trim());
buf = "";
} else if (ch === "}") {
const selector = stack.pop() ?? "";
blocks.push({ path: [...stack, selector], body: buf });
buf = "";
} else {
buf += ch;
}
}
for (const block of blocks) {
block.decls = [];
for (const m of block.body.matchAll(/(--[\w-]+)\s*:\s*([^;]+);/g)) {
block.decls.push({
name: m[1],
authored: m[2].trim(),
hex: hexes.get(m[1]) ?? null,
});
}
}
return blocks;
}
const resolve = (name) => getComputedStyle(document.documentElement).getPropertyValue(name).trim();
const probe = document.createElement("div");
probe.style.cssText = "position:absolute;visibility:hidden;height:0";
document.body.append(probe);
function toPx(value) {
probe.style.width = "0";
probe.style.width = value;
const px = probe.getBoundingClientRect().width;
return Number.isFinite(px) && px > 0 ? px : 0;
}
const el = (tag, props = {}, ...children) => {
const node = Object.assign(document.createElement(tag), props);
for (const child of children.flat()) {
if (child == null || child === false) continue;
node.append(child);
}
return node;
};
const name = (token) => el("span", { className: "token-name", textContent: token });
const value = (text) => el("span", { className: "token-value", textContent: text });
const badge = (text, variant) =>
el("span", {
className: variant ? `badge badge--${variant}` : "badge",
textContent: text,
});
const rung = (token, prefix) => token.slice(prefix.length);
// The background colors, text and item colors colors, and the WCAG threshold for readability.
const COLOR_GROUPS = [
{
title: "Brand",
tokens: [
"--ak-global--color--accent",
"--ak-global--color--primary",
"--ak-global--color--primary--hover",
],
against: "--ak-global--color--surface",
threshold: 3,
},
{
title: "Text",
tokens: ["--ak-global--color--text", "--ak-global--color--text--muted"],
against: "--ak-global--color--surface",
threshold: 4.5,
},
{
title: "Link",
tokens: [
"--ak-global--color--link",
"--ak-global--color--link--hover",
"--ak-global--color--link--visited",
],
against: "--ak-global--color--surface",
threshold: 4.5,
},
{
title: "Surface",
tokens: [
"--ak-global--color--surface",
"--ak-global--color--surface--raised",
"--ak-global--color--surface--muted",
],
against: "--ak-global--color--text",
threshold: 4.5,
ringed: true,
},
{
title: "Border",
tokens: ["--ak-global--color--border", "--ak-global--color--border--strong"],
against: "--ak-global--color--surface",
threshold: 3,
},
{
title: "Status",
tokens: [
"--ak-global--color--info",
"--ak-global--color--success",
"--ak-global--color--warning",
"--ak-global--color--danger",
],
against: "--ak-global--color--surface",
threshold: 3,
},
];
// Patternfly icons, since these aren't encoded in either Fonts or Theme.
const PF_ICONS = [
["user", 0xe91e],
["users", 0xe91f],
["key", 0xe924],
["locked", 0xe923],
["unlocked", 0xe922],
["security", 0xe946],
["private", 0xe914],
["enterprise", 0xe906],
["server", 0xe90d],
["cluster", 0xe620],
["network", 0xe909],
["domain", 0xe919],
["applications", 0xe936],
["catalog", 0xe953],
["blueprint", 0xe915],
["automation", 0xe937],
["integration", 0xe948],
["migration", 0xe931],
["bell", 0xe952],
["ok", 0xe602],
["error-circle-o", 0xe926],
["warning-triangle", 0xe975],
["info", 0xe92b],
["help", 0xe605],
["history", 0xe617],
["edit", 0xe60a],
["filter", 0xe943],
["export", 0xe616],
["import", 0xe615],
["plugged", 0xe96a],
["connected", 0xe938],
["disconnected", 0xe955],
["pending", 0xe964],
["in-progress", 0xe933],
["topology", 0xe608],
["module", 0xe959],
["tenant", 0xe916],
["task", 0xe974],
["project", 0xe96c],
["repository", 0xe90b],
["globe-route", 0xe958],
["monitoring", 0xe95a],
];
const FA_ICONS = [
["sign-in-alt", 0xf2f6],
["sign-out-alt", 0xf2f5],
["fingerprint", 0xf577],
["id-card", 0xf2c2],
["id-badge", 0xf2c1],
["shield-alt", 0xf3ed],
["user-shield", 0xf505],
["user-lock", 0xf502],
["lock", 0xf023],
["lock-open", 0xf3c1],
["unlock", 0xf09c],
["key", 0xf084],
["envelope", 0xf0e0],
["mobile-alt", 0xf3cd],
["qrcode", 0xf029],
["check-circle", 0xf058],
["times-circle", 0xf057],
["exclamation-circle", 0xf06a],
["exclamation-triangle", 0xf071],
["question-circle", 0xf059],
["info-circle", 0xf05a],
["cog", 0xf013],
["cogs", 0xf085],
["search", 0xf002],
["plus", 0xf067],
["trash", 0xf1f8],
["pencil-alt", 0xf303],
["link", 0xf0c1],
["external-link-alt", 0xf35d],
["clock", 0xf017],
["calendar-alt", 0xf073],
["code", 0xf121],
["terminal", 0xf120],
["database", 0xf1c0],
["cloud", 0xf0c2],
["globe", 0xf0ac],
["download", 0xf019],
["upload", 0xf093],
["sync", 0xf021],
["redo", 0xf01e],
["eye", 0xf06e],
["eye-slash", 0xf070],
["bars", 0xf0c9],
["ellipsis-v", 0xf142],
["chevron-right", 0xf054],
["chevron-down", 0xf078],
["arrow-right", 0xf061],
["copy", 0xf0c5],
["file-alt", 0xf15c],
["clipboard-check", 0xf46c],
];
const PANGRAM = "Sphinx of black quartz, judge my vow";
const PROSE =
"authentik is an IdP (Identity Provider) and SSO (Single Sign-On) platform that is built with security at the forefront of every piece of code and every feature, with an emphasis on flexibility and versatility.";
const blocks = parseStylesheet(await fetch(CSS_URL).then((r) => r.text()));
/** The unconditional :root block — the canonical token list, in order. */
const rootBlock = blocks.find((b) => b.path.length === 1 && b.path[0].includes(":root"));
if (!rootBlock) throw new Error(`no top-level :root block in ${CSS_URL}`);
const TOKENS = rootBlock.decls;
const darkNames = new Set(
blocks
.filter((b) => b.path.at(-1) === DARK_SELECTOR)
.flatMap((b) => b.decls.map((d) => d.name)),
);
const mediaNames = new Set(
blocks
.filter((b) => b.path.some((p) => p.startsWith("@media")))
.flatMap((b) => b.decls.map((d) => d.name)),
);
const byPrefix = (prefix) => TOKENS.filter((t) => t.name.startsWith(prefix));
/** Tokens the page has actually rendered, for the audit at the end. */
const rendered = new Set();
const mark = (token) => {
rendered.add(token.name ?? token);
return token;
};
// ------------------------------------------------------------------
// Color.
// ------------------------------------------------------------------
function contrastBadge(swatchToken, counterpartToken, threshold) {
const ratio = contrastRatio(
parseColor(resolve(swatchToken)),
parseColor(resolve(counterpartToken)),
);
if (ratio == null) return null;
const variant = ratio >= 4.5 ? "pass" : ratio >= threshold ? "mid" : "fail";
const other = rung(counterpartToken, "--ak-global--color--");
return badge(`${ratio.toFixed(2)}:1 vs ${other}`, variant);
}
function renderColors() {
const host = document.getElementById("color-groups");
host.replaceChildren();
const claimed = new Set(COLOR_GROUPS.flatMap((g) => g.tokens));
const groups = [...COLOR_GROUPS];
const orphans = byPrefix("--ak-global--color--")
.filter((t) => !claimed.has(t.name))
.map((t) => t.name);
if (orphans.length) {
groups.push({
title: "Ungrouped",
tokens: orphans,
against: "--ak-global--color--surface",
threshold: 3,
});
}
for (const group of groups) {
const cells = group.tokens
.map((tokenName) => {
const token = TOKENS.find((t) => t.name === tokenName);
if (!token) return null;
mark(token);
const effective = resolve(tokenName);
const rgb = parseColor(effective);
const overridden = darkNames.has(tokenName);
// The :root definition may be a var() alias, but a
// theme block can replace it with a literal — so
// the alias only holds while that block is inactive.
const inDark = document.documentElement.dataset.theme === "dark";
const isAlias = token.authored.startsWith("var(") && !(inDark && overridden);
return el(
"div",
{ className: "swatch" },
el("div", {
className: "swatch__chip" + (group.ringed ? " swatch__chip--ringed" : ""),
style: `background:${effective}`,
}),
el(
"div",
{ className: "swatch__body" },
name(tokenName),
value(effective),
el(
"div",
{ className: "badges" },
token.hex ? badge(token.hex) : badge(toHex(rgb)),
isAlias
? badge(
`alias of ${token.authored
.match(/var\(\s*(--[\w-]+)/)[1]
.replace("--ak-global--color--", "")}`,
)
: null,
// Contrast is symmetric, so the swatch
// and its counterpart are just the pair.
contrastBadge(tokenName, group.against, group.threshold),
!overridden && !isAlias
? badge("no dark override", "fallthrough")
: null,
),
),
);
})
.filter(Boolean);
host.append(
el("h3", { className: "subhead", textContent: group.title }),
el("div", { className: "grid" }, cells),
);
}
document.getElementById("color-count").textContent =
`${byPrefix("--ak-global--color--").length} tokens`;
}
// ------------------------------------------------------------------
// Typography.
// ------------------------------------------------------------------
function renderTypography() {
const families = byPrefix("--ak-global--font-family--");
const sizes = byPrefix("--ak-global--font-size--");
const weights = byPrefix("--ak-global--font-weight--");
const leadings = byPrefix("--ak-global--line-height--");
document.getElementById("families").replaceChildren(
...families.map((token) => {
mark(token);
const effective = resolve(token.name);
return el(
"div",
{ className: "specimen" },
el("div", {
className: "specimen__display",
style: `font-family:${effective}`,
textContent: "Aa",
}),
name(token.name),
el("br"),
value(token.authored.startsWith("var(") ? token.authored : effective),
el("p", {
className: "specimen__para",
style: `font-family:${effective}`,
textContent: PANGRAM,
}),
);
}),
);
document.getElementById("sizes").replaceChildren(
...sizes.map((token) => {
mark(token);
const effective = resolve(token.name);
return el(
"div",
{ className: "ramp__row" },
name(token.name),
value(`${effective} · ${toPx(effective).toFixed(1)}px`),
el("div", {
className: "ramp__sample",
style: `font-size:${effective}`,
textContent: PANGRAM,
}),
);
}),
);
const FACES = [
["--ak-global--font-family--display", "RedHatDisplay", "300900"],
["--ak-global--font-family--sans-serif", "RedHatText", "400500"],
["--ak-global--font-family--monospace", "RedHatMono", "300700"],
];
document.getElementById("weights").replaceChildren(
...FACES.map(([familyToken, face, range]) =>
el(
"div",
{ className: "weights__col" },
el("h4", { textContent: `${face} — declared ${range}` }),
...weights.map((token) => {
mark(token);
const w = resolve(token.name);
return el(
"div",
{ className: "weights__row" },
el("span", {
style: `font-family:${resolve(familyToken)};font-weight:${w}`,
textContent: "Identity",
}),
value(`${rung(token.name, "--ak-global--font-weight--")} ${w}`),
);
}),
),
),
);
document.getElementById("leading").replaceChildren(
...leadings.map((token) => {
mark(token);
const effective = resolve(token.name);
return el(
"div",
{ className: "specimen" },
name(token.name),
": ",
value(effective),
el("p", {
className: "specimen__para",
style: `line-height:${effective}`,
textContent: PROSE,
}),
);
}),
);
document.getElementById("type-count").textContent =
`${families.length + sizes.length + weights.length + leadings.length} tokens`;
}
// ------------------------------------------------------------------
// Spacing, shape.
// ------------------------------------------------------------------
function renderSpacing() {
const spaces = byPrefix("--ak-global--space--");
document.getElementById("spaces").replaceChildren(
...spaces.flatMap((token) => {
mark(token);
const effective = resolve(token.name);
return [
name(token.name),
value(`${effective} · ${toPx(effective).toFixed(0)}px`),
el("div", { className: "bar", style: `width:${effective}` }),
];
}),
);
document.getElementById("space-count").textContent = `${spaces.length} tokens`;
}
function renderShape() {
const radii = byPrefix("--ak-global--radius--");
const strokes = byPrefix("--ak-global--border-width--");
document.getElementById("radii").replaceChildren(
...radii.map((token) => {
mark(token);
const effective = resolve(token.name);
return el(
"div",
{ className: "shape-chip" },
el("div", {
className: "shape-chip__box",
style: `border-radius:${effective}`,
}),
name(token.name),
value(effective),
);
}),
);
document.getElementById("strokes").replaceChildren(
...strokes.map((token) => {
mark(token);
const effective = resolve(token.name);
return el(
"div",
{ className: "shape-chip" },
el("div", {
className: "stroke-chip__box",
style: `border-width:${effective}`,
}),
name(token.name),
value(effective),
);
}),
);
document.getElementById("shape-count").textContent = `${radii.length + strokes.length} tokens`;
}
// ------------------------------------------------------------------
// Surfaces, shadows.
// ------------------------------------------------------------------
function renderSurfaces() {
const planes = byPrefix("--ak-global--color--surface");
const shadows = byPrefix("--ak-global--shadow--");
document.getElementById("surface-planes").replaceChildren(
...planes.map((token) => {
const effective = resolve(token.name);
return el(
"div",
{
className: "surface-demo",
style: `background:${effective}`,
},
name(token.name),
el("br"),
value(effective),
);
}),
);
document.getElementById("shadows").replaceChildren(
...shadows.map((token) => {
mark(token);
const effective = resolve(token.name);
return el(
"div",
{ className: "shadow-card", style: `box-shadow:${effective}` },
name(token.name),
badge(
darkNames.has(token.name) ? "themed" : "no dark override",
darkNames.has(token.name) ? null : "fallthrough",
),
);
}),
);
document.getElementById("surface-count").textContent =
`${planes.length} planes · ${shadows.length} shadows`;
}
// ------------------------------------------------------------------
// Motion.
// ------------------------------------------------------------------
function renderMotion() {
const motion = [...byPrefix("--ak-global--duration--"), ...byPrefix("--ak-global--easing--")];
document.getElementById("motion-tokens").replaceChildren(
...motion.map((token) => {
mark(token);
return el(
"div",
{ className: "specimen" },
name(token.name),
el("br"),
value(resolve(token.name)),
el(
"div",
{ className: "badges" },
mediaNames.has(token.name) ? badge("zeroed by prefers-reduced-motion") : null,
),
);
}),
);
const prefersReduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
document.getElementById("motion-state").textContent =
` — --ak-global--duration--normal resolves to ${resolve("--ak-global--duration--normal")}; ` +
`prefers-reduced-motion is ${prefersReduced ? "on" : "off"}; ` +
`data-theme is "${document.documentElement.dataset.theme || "(unset)"}". ` +
`Note that data-theme holds one value, so "dark" and "reduced" cannot both apply.`;
document.getElementById("motion-count").textContent = `${motion.length} tokens`;
}
// ------------------------------------------------------------------
// Layering.
// ------------------------------------------------------------------
function renderLayering() {
const zs = byPrefix("--ak-global--z-index--");
document.getElementById("zstack").replaceChildren(
...zs.map((token, i) => {
mark(token);
const effective = resolve(token.name);
return el(
"div",
{
className: "zstack__layer",
style: `left:${i * 4.5}rem;z-index:${effective}`,
},
name(token.name),
value(effective),
);
}),
);
document.getElementById("z-count").textContent = `${zs.length} tokens`;
}
// ------------------------------------------------------------------
// Icons.
// ------------------------------------------------------------------
function renderIcons() {
const cells = (set, variant, prefix) =>
set.map(([iconName, cp]) =>
el(
"div",
{ className: "icon-cell" },
el("i", {
className: `glyph glyph--${variant}`,
textContent: String.fromCodePoint(cp),
ariaHidden: "true",
}),
el("span", {
className: "icon-cell__name",
textContent: `${prefix}${iconName}`,
}),
value(`\\${cp.toString(16)}`),
),
);
document.getElementById("icons-pf").replaceChildren(...cells(PF_ICONS, "pf", "pf-icon-"));
document.getElementById("icons-fa").replaceChildren(...cells(FA_ICONS, "fa", "fa-"));
document.getElementById("icon-count").textContent =
`${PF_ICONS.length} pficon · ${FA_ICONS.length} fa-solid, of the full sets`;
}
// ------------------------------------------------------------------
// Overview and audit.
// ------------------------------------------------------------------
function renderOverview() {
const categories = new Set(
TOKENS.map((t) => t.name.replace(/^--ak-global--/, "").split("--")[0]),
);
console.log("C:", categories);
document.getElementById("stats").replaceChildren(
...[
[TOKENS.length, "tokens in :root"],
[categories.size, "categories"],
[darkNames.size, "dark overrides"],
[byPrefix("--ak-global--color--").length, "color tokens"],
[TOKENS.filter((t) => t.authored.startsWith("var(")).length, "aliases"],
].map(([v, label]) =>
el(
"div",
{ className: "stat" },
el("div", { className: "stat__value", textContent: String(v) }),
el("div", { className: "stat__label", textContent: label }),
),
),
);
document.getElementById("tokens-count").textContent = `${TOKENS.length} in :root`;
document.getElementById("meta-line").textContent =
`${TOKENS.length} tokens · ${darkNames.size} dark overrides · design system reference`;
}
function renderAudit() {
const host = document.getElementById("audit");
const missing = TOKENS.filter((t) => !rendered.has(t.name)).map((t) => t.name);
const unknownDark = [...darkNames].filter((n) => !TOKENS.some((t) => t.name === n));
host.replaceChildren();
if (!missing.length && !unknownDark.length) {
host.dataset.state = "ok";
host.append(
el("strong", {
textContent: `Complete: all ${TOKENS.length} tokens in dist/index.css are represented above.`,
}),
);
return;
}
host.dataset.state = missing.length ? "stale" : "error";
host.append(el("strong", { textContent: "This page is out of date with the build." }));
if (missing.length) {
host.append(
el(
"div",
{},
`${missing.length} token(s) exist in dist/index.css but no section renders them:`,
el("ul", {}, ...missing.map((n) => el("li", {}, el("code", { textContent: n })))),
),
);
}
if (unknownDark.length) {
host.append(
el(
"div",
{},
`${unknownDark.length} token(s) are defined only in ${DARK_SELECTOR}, with no :root default:`,
el(
"ul",
{},
...unknownDark.map((n) => el("li", {}, el("code", { textContent: n }))),
),
),
);
}
}
function renderThemed() {
rendered.clear();
renderOverview();
renderColors();
renderTypography();
renderSpacing();
renderShape();
renderSurfaces();
renderMotion();
renderLayering();
renderAudit();
}
for (const button of document.querySelectorAll("[data-theme-value]")) {
button.addEventListener("click", () => {
document.documentElement.dataset.theme = button.dataset.themeValue;
for (const other of document.querySelectorAll("[data-theme-value]")) {
other.setAttribute("aria-pressed", String(other === button));
}
renderThemed();
});
}
const track = document.getElementById("motion-track");
document.getElementById("motion-run").addEventListener("click", () => {
track.dataset.run = track.dataset.run === "true" ? "false" : "true";
});
renderIcons();
renderThemed();
document.fonts?.ready.then(renderThemed);

View File

@@ -0,0 +1,257 @@
<!doctype html>
<html lang="en" data-theme="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>@goauthentik/theme — design system reference</title>
<!-- Copied from the `fonts` package -->
<link rel="stylesheet" href="./faces.css" />
<link rel="stylesheet" href="./icons.css" />
<!-- This document -->
<link rel="stylesheet" href="./index.css" />
<link rel="stylesheet" href="./demo.css" />
</head>
<body>
<header class="masthead">
<div class="masthead__inner">
<h1>@goauthentik/<span class="accent">theme</span></h1>
<span class="masthead__meta" id="meta-line">reading dist/index.css…</span>
<div class="masthead__controls">
<div class="segmented" role="group" aria-label="Theme">
<button type="button" data-theme-value="" aria-pressed="true">Light</button>
<button type="button" data-theme-value="dark" aria-pressed="false">
Dark
</button>
<button type="button" data-theme-value="reduced" aria-pressed="false">
Reduced Motion
</button>
</div>
</div>
</div>
<nav class="sitenav" aria-label="Sections">
<a href="#tokens">Tokens</a>
<a href="#color">Color</a>
<a href="#typography">Typography</a>
<a href="#spacing">Spacing</a>
<a href="#shape">Shape</a>
<a href="#surfaces">Surfaces</a>
<a href="#motion">Motion</a>
<a href="#layering">Layering</a>
<a href="#icons">Icons</a>
</nav>
</header>
<main>
<section id="tokens">
<div class="section__head">
<h2>Tokens</h2>
<span class="section__count" id="tokens-count"></span>
<p class="section__note">
Following the pattern established by Patternfly, this page represents all of
the tokens we currently have in <code>@goauthentik/theme</code>. The
<code>build-demo.mjs</code> script copies the font and CSS files here, but
this is the best demo we have so far of the tokens we support.
</p>
</div>
<div class="stats" id="stats"></div>
<div class="audit" id="audit">Auditing…</div>
</section>
<section id="color">
<div class="section__head">
<h2>Color</h2>
<span class="section__count" id="color-count"></span>
<p class="section__note">
We keep our colors in <code>oklch</code>, but include the hex as a comment
to assist IDEs. Contrast is computed from the <code>oklch</code> triplet —
converted through Oklab to linear sRGB, gamut-clamped, then measured per
WCAG 2.1 — against the surface token appropriate to each role, and
re-measured whenever the theme changes. Tokens marked
<span class="badge badge--fallthrough">no dark override</span> keep their
light value in dark mode. Most of these are fine in dark mode, but you
should double-check.
</p>
</div>
<div id="color-groups"></div>
</section>
<section id="typography">
<div class="section__head">
<h2>Typography</h2>
<span class="section__count" id="type-count"></span>
<p class="section__note">
Three RedHat variable families, loaded from
<code>@goauthentik/fonts</code>. The semantic aliases (<code>body</code>,
<code>heading</code>, <code>code</code>) are listed ahead of the primitives
they point at, matching the order in the source.
</p>
</div>
<h3 class="subhead">Families</h3>
<div class="grid" id="families"></div>
<h3 class="subhead">Size ramp</h3>
<div class="ramp" id="sizes"></div>
<h3 class="subhead">Weights, across all three families</h3>
<p class="section__note" style="margin-bottom: var(--ak-global--space--md)">
Each family is rendered at every weight token.
<code>faces.css</code> declares different variable ranges per family — Display
300900, Text 400500, Mono 300700 — so a requested weight outside a family's
range gets clamped, and the browser may synthesise the difference. Rows that
look identical are the clamp; the computed weight beside each row is what was
asked for, not necessarily what the face can draw.
</p>
<div class="weights" id="weights"></div>
<h3 class="subhead">Line height</h3>
<div class="leading" id="leading"></div>
</section>
<section id="spacing">
<div class="section__head">
<h2>Spacing</h2>
<span class="section__count" id="space-count"></span>
<p class="section__note">
Bar length is the literal token value, so the ramp's shape is the ramp's
actual proportions. Pixel figures are resolved at the current root font
size.
</p>
</div>
<div class="bars" id="spaces"></div>
</section>
<section id="shape">
<div class="section__head">
<h2>Shape</h2>
<span class="section__count" id="shape-count"></span>
<p class="section__note">
Corner radii and border widths. Both scales are deliberately short; the
radius scale has an
<code>sm</code> and a full pill and nothing between them, which is why every
card on this page shares one corner.
</p>
</div>
<h3 class="subhead">Radius</h3>
<div class="grid" id="radii"></div>
<h3 class="subhead">Border width</h3>
<div class="grid" id="strokes"></div>
</section>
<section id="surfaces">
<div class="section__head">
<h2>Surfaces &amp; elevation</h2>
<span class="section__count" id="surface-count"></span>
<p class="section__note">
Three background planes and five shadows. The shadow tokens carry their own
dark-theme values — deeper alpha rather than a different geometry — so
elevation stays legible on a dark ground.
</p>
</div>
<h3 class="subhead">Planes</h3>
<div class="surfaces" id="surface-planes"></div>
<h3 class="subhead">Shadows</h3>
<div class="shadow-stage" id="shadows"></div>
</section>
<section id="motion">
<div class="section__head">
<h2>Motion</h2>
<span class="section__count" id="motion-count"></span>
<p class="section__note">
One duration and one easing curve. Both the
<code>prefers-reduced-motion</code> media query and the
<code>reduced</code> theme zero the duration — and because they are two
different mechanisms writing the same variable, the state readout below
reports each separately.
</p>
</div>
<div class="grid" id="motion-tokens"></div>
<h3 class="subhead">In practice</h3>
<div class="motion-track" id="motion-track" data-run="false">
<div class="motion-dot"></div>
</div>
<p class="section__note" style="margin-top: var(--ak-global--space--md)">
<button type="button" id="motion-run" class="badge" style="cursor: pointer">
Run transition
</button>
<span id="motion-state"></span>
</p>
</section>
<section id="layering">
<div class="section__head">
<h2>Layering</h2>
<span class="section__count" id="z-count"></span>
<p class="section__note">
Six stacking rungs, 100 apart. The gaps are the point: they leave room for
ad-hoc values between named layers without renumbering the scale.
</p>
</div>
<div class="zstack" id="zstack"></div>
</section>
<section id="icons">
<div class="section__head">
<h2>Icons</h2>
<span class="section__count" id="icon-count"></span>
<p class="section__note">
<code>@goauthentik/fonts</code> ships the <code>@font-face</code> rules and
the font files, and stops there — the <code>.pf-icon-*</code> and
<code>.fa</code> classes stay with PatternFly, which this package does not
depend on. What follows is therefore a representative selection addressed by
raw codepoint, not the complete sets. The codepoints were taken from
<code>@patternfly/patternfly@4.224.5</code>, whose font binaries are
byte-identical to the ones shipped here, so they are authoritative for these
files.
</p>
</div>
<h3 class="subhead">pficon</h3>
<div class="icons" id="icons-pf"></div>
<h3 class="subhead">Font Awesome 5 Free, solid</h3>
<div class="icons" id="icons-fa"></div>
</section>
</main>
<footer>
<p>
Generated from <code>packages/theme/dist/index.css</code>,
<code>packages/fonts/faces.css</code>, and <code>packages/fonts/icons.css</code>.
</p>
<table class="provenance">
<thead>
<tr>
<th>Asset</th>
<th>Origin</th>
<th>License</th>
</tr>
</thead>
<tbody>
<tr>
<td>RedHatDisplay, RedHatText, RedHatMono</td>
<td>The Red Hat Project Authors</td>
<td>SIL OFL 1.1</td>
</tr>
<tr>
<td>pficon</td>
<td>PatternFly, © Red Hat, Inc.</td>
<td>MIT</td>
</tr>
<tr>
<td>Font Awesome 5 Free (solid)</td>
<td>Fonticons, Inc.</td>
<td>SIL OFL 1.1 (fonts), CC BY 4.0 (icons)</td>
</tr>
</tbody>
</table>
<p>
Full attribution in <code>packages/fonts/NOTICE.md</code>; license texts in
<code>packages/fonts/licenses/</code>.
</p>
</footer>
<script src="./demo.js" type="module"></script>
</body>
</html>

View File

@@ -0,0 +1,49 @@
/**
* @file ESLint Configuration
*
* @import { Config } from "eslint/config";
*/
import js from "@eslint/js";
import { defineConfig } from "eslint/config";
import tseslint from "typescript-eslint";
// @ts-check
/**
* @type {Config[]}
*/
const eslintConfig = defineConfig(
// Global ignores: compiled output is generated, not linted.
{ ignores: ["dist/**"] },
{
extends: [js.configs.recommended, tseslint.configs.recommended],
rules: {
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-expect-error": "allow-with-description",
"ts-ignore": true,
"ts-nocheck": "allow-with-description",
"ts-check": false,
"minimumDescriptionLength": 5,
},
],
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": "error",
"no-invalid-this": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
},
},
);
export default eslintConfig;

View File

@@ -0,0 +1,26 @@
/**
* @file ESLint Configuration
*
* @import { Config } from "eslint/config";
*/
import css from "@eslint/css";
// @ts-check
/**
* @type {Config[]}
*/
export default [
{
files: ["dist/*.css"],
plugins: {
css,
},
language: "css/css",
rules: {
"css/no-duplicate-imports": "error",
},
},
];

188
packages/theme/package.json Normal file
View File

@@ -0,0 +1,188 @@
{
"name": "@goauthentik/theme",
"version": "1.0.0",
"description": "Styleframe-based design system for authentik.",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/goauthentik/authentik.git",
"directory": "packages/theme"
},
"scripts": {
"build": "wireit",
"build:assets": "node ./build.mjs",
"build:demo": "node ./build-demo.mjs",
"build:dtcg": "styleframe dtcg export --config styleframe.config.ts --output dist/dtcg --collection authentik",
"build:format": "prettier --write ./dist",
"build:src": "tsgo -p .",
"check": "run-s lint-check lint-output",
"clean": "rimraf ./public",
"demo": "run-s build build:demo demo:run",
"demo:run": "http-server ./public -p 8001 -i",
"lint": "eslint --fix .",
"lint:css": "eslint --max-warnings 0 --config eslint.css.mjs .",
"lint-check": "eslint --max-warnings 0 .",
"lint-output": "run-s build:src build:assets lint:css",
"prettier": "prettier --write .",
"prettier-check": "prettier --check .",
"watch": "tsgo -p . --watch"
},
"type": "module",
"types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"node": "./src/index.ts",
"bundler": "./src/index.ts",
"default": "./dist/index.js"
},
"./package.json": "./package.json",
"./types": "./src/index.ts",
"./node": {
"types": "./src/node.ts",
"node": "./src/node.ts",
"bundler": "./src/node.ts",
"default": "./dist/node.js"
},
"./browser": {
"types": "./src/index.ts",
"node": "./src/index.ts",
"bundler": "./src/index.ts",
"default": "./dist/index.js"
},
"./index.css": "./dist/index.css",
"./color.css": "./dist/color.css",
"./typography.css": "./dist/typography.css",
"./spacing.css": "./dist/spacing.css",
"./shape.css": "./dist/shape.css",
"./shadow.css": "./dist/shadow.css",
"./motion.css": "./dist/motion.css",
"./z-index.css": "./dist/z-index.css",
"./tokens.dtcg.json": "./dist/dtcg/tokens.json",
"./tokens.dtcg.resolver.json": "./dist/dtcg/tokens.resolver.json",
"./tokens": {
"types": "./src/index.ts",
"node": "./src/index.ts",
"bundler": "./src/index.ts",
"default": "./dist/index.js"
},
"./build": {
"types": "./src/node.ts",
"node": "./src/node.ts",
"bundler": "./src/node.ts",
"default": "./dist/node.js"
}
},
"dependencies": {
"@goauthentik/fonts": "link:../fonts",
"@styleframe/core": "^3.5.0",
"@styleframe/dtcg": "^1.1.0",
"@styleframe/theme": "^3.7.0",
"@styleframe/transpiler": "^3.3.0",
"http-server": "^14.1.1",
"rimraf": "^6.1.3",
"styleframe": "^3.7.0"
},
"devDependencies": {
"@eslint/css": "^1.3.0",
"@eslint/js": "catalog:",
"@goauthentik/eslint-config": "link:../eslint-config",
"@goauthentik/prettier-config": "link:../prettier-config",
"@goauthentik/tsconfig": "link:../tsconfig",
"@styleframe/cli": "^4.0.0",
"@types/culori": "^4.0.1",
"@types/node": "catalog:",
"@typescript/native-preview": "7.0.0-dev.20260707.2",
"culori": "^4.0.2",
"eslint": "catalog:",
"npm-run-all": "^4.1.5",
"prettier": "catalog:",
"prettier-plugin-packagejson": "catalog:",
"typescript": "catalog:",
"typescript-eslint": "catalog:",
"wireit": "^0.14.13"
},
"files": [
"src/**/*",
"dist/**/*"
],
"wireit": {
"build": {
"command": "run-s build:src build:assets build:dtcg build:format",
"files": [
"src/**/*.ts",
"build.mjs",
"styleframe.config.ts",
"tsconfig.json",
"package.json"
],
"output": [
"dist/**"
]
}
},
"engines": {
"node": ">=24"
},
"devEngines": {
"runtime": {
"name": "node",
"version": ">=24",
"onFail": "ignore"
},
"packageManager": {
"name": "pnpm",
"version": ">=11.5.1",
"onFail": "ignore"
}
},
"prettier": "@goauthentik/prettier-config",
"publishConfig": {
"access": "public",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./package.json": "./package.json",
"./types": "./dist/index.d.ts",
"./node": {
"types": "./dist/node.d.ts",
"import": "./dist/node.js"
},
"./browser": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./index.css": "./dist/index.css",
"./color.css": "./dist/color.css",
"./typography.css": "./dist/typography.css",
"./spacing.css": "./dist/spacing.css",
"./shape.css": "./dist/shape.css",
"./shadow.css": "./dist/shadow.css",
"./motion.css": "./dist/motion.css",
"./z-index.css": "./dist/z-index.css",
"./tokens.dtcg.json": "./dist/dtcg/tokens.json",
"./tokens.dtcg.resolver.json": "./dist/dtcg/tokens.resolver.json",
"./tokens": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./build": {
"types": "./dist/node.d.ts",
"import": "./dist/node.js"
}
},
"types": "./dist/index.d.ts"
},
"#exports-comment": [
"The TypeScript entry points resolve to src/ for tooling that can read",
"TypeScript directly — tsc via `types`, node via jiti/tsgo, and bundlers",
"that opt into the `bundler` condition (see web/scripts/build-web.mjs).",
"`default` keeps the built dist/ output for plain Node and any consumer",
"that does not set a condition; publishConfig.exports points every entry",
"at dist/ for the published tarball.",
"The CSS and DTCG entries have no source form — they are generated by",
"build.mjs and the styleframe CLI — so they always resolve to dist/."
]
}

View File

@@ -0,0 +1,13 @@
/**
* @file Public entry point for `@goauthentik/theme`.
*
* Importing this module registers every token against the shared styleframe
* instance and re-exports the handles + primitives consumers need to author
* component styles or build CSS.
*
* Browser-safe: no Node-only dependencies. Build helpers that touch the
* filesystem live in `./node.ts` (exposed via the `./build` subpath).
*/
export { instance, ref, selector, theme, variable } from "./shared.js";
export * from "./tokens/index.js";

View File

@@ -0,0 +1,59 @@
/*
* @file Node-only build helpers for `@goauthentik/theme`.
*
* Re-exports everything the browser entry exports, plus filesystem-touching
* helpers that wrap styleframe's `transpile()` for use from build scripts.
*
*/
import { writeFile } from "node:fs/promises";
import { instance } from "./shared.js";
import type { OutputFile } from "@styleframe/transpiler";
import { transpile } from "@styleframe/transpiler";
// Re-export everything the browser entry exposes so consumers can do either
// `import { variable } from "@goauthentik/theme"` (browser-safe) or
// `import { build } from "@goauthentik/theme/build"` (Node-only) from the
// same package without juggling subpaths.
export * from "./shared.js";
export * from "./tokens/index.js";
interface BuildOptions {
/**
* If provided, the generated CSS is written to this absolute path in
* addition to being returned.
*/
outFile?: string;
}
interface BuildResult {
/**
* The full CSS string emitted by styleframe.
*/
css: string;
/**
* Raw transpile output for callers that want to inspect every file
* styleframe produced.
*/
files: OutputFile[];
}
/**
* Transpile the configured token tree to CSS.
*
* Equivalent to `npx styleframe build` for the css output type, but returns
* the string directly so build scripts can post-process before writing.
*/
export async function build(options: BuildOptions = {}): Promise<BuildResult> {
const output = await transpile(instance, { type: "css" });
const cssFile = output.files.find((file) => file.name.endsWith(".css"));
const css = cssFile?.content ?? "";
if (options.outFile) {
await writeFile(options.outFile, css, "utf-8");
}
return { css, files: output.files };
}

View File

@@ -0,0 +1,54 @@
/**
* @file Styleframe instance and shared primitives for the authentik theme.
*
* This module configures one global {@link Styleframe} instance and re-exports
* its primitives ({@link variable}, {@link theme}, {@link ref}, {@link selector},
* etc.) for use by the per-category token modules under `./tokens/`.
*
* The instance is configured so that:
*
* - Variable names use the `--ak-*` prefix authentik components and brand custom
* CSS expect. Source tokens are written in dot-notation (`color.primary`)
* and the configured name function rewrites that to `ak-color-primary` before
* styleframe prepends the leading `--`.
* - The theme selector matches the existing `html[data-theme="..."]` convention
* used across the authentik stylesheets.
*
*/
import { styleframe, type StyleframeOptions } from "styleframe";
/**
* authentik-specific styleframe configuration.
*/
export const authentikStyleframeOptions: StyleframeOptions = {
indent: " ",
variables: {
name: ({ name }: { name: string }) => `ak-global--${name.replace(/\./g, "--")}`,
},
themes: {
selector: ({ name }: { name: string }) => `html[data-theme="${name}"]`,
},
};
/**
* Singleton styleframe instance used by every token module.
*
* Importing any module under `./tokens/` triggers the side-effects that
* register variables and themes against this instance.
*/
export const instance = styleframe(authentikStyleframeOptions);
export const {
variable,
theme,
ref,
selector,
atRule,
keyframes,
media,
css,
utility,
modifier,
recipe,
} = instance;

View File

@@ -0,0 +1,31 @@
import { createUseVariable } from "@styleframe/theme";
import { formatHex, oklch as toOklch, toGamut } from "culori";
import { createVariableFunction, type Reference } from "styleframe";
export type Variable = ReturnType<ReturnType<typeof createVariableFunction>>;
export type VPPair = [Variable, string];
/*
* Restrict the OKLCH values to six decimal places; Javascript will give it to you accurate to 16
* places, but OKLCH doesn't much care past six, and 16 is just unreadable.
*
* Includes in each OKLCH output line a comment containing the RGB value, to help IDEs show the
* color accurately.
*/
const toSrgb = toGamut("rgb", "oklch");
const round = (v: number, precision = 6) => Number(v.toFixed(precision)).toString();
export const oklchTransform = (value: string | Reference<string>) => {
if (typeof value !== "string") {
return value;
}
const c = toOklch(value);
if (!c || c.l === null || c.c === null) {
return value;
}
const result = `oklch(${round(c.l)} ${round(c.c)} ${round(c.h ?? 0)} / ${round(c.alpha ?? 1)})`;
return `${result} /* ${formatHex(toSrgb(c))} */`;
};
export const useColorDesignTokens = createUseVariable("color", { transform: oklchTransform });

View File

@@ -0,0 +1,78 @@
/**
* @file Color tokens — semantic surface, text, state, and brand colors.
*
* Light values are declared via `variable()`. Dark values are declared inside
* the `dark` theme block so they emit under `html[data-theme="dark"]`.
*
* Link tokens are wired through `ref()` so brand overrides to `color.primary`
* cascade to links without separate overrides. The dark theme intentionally
* re-points links to their own values rather than chaining through primary
* because dark mode links need higher luminance than primary buttons.
*
* `warning` and `danger` deliberately stay on light values in dark mode — state
* colors keep consistent intensity across themes so warnings read as urgent.
*
* Values are authored as hex and transformed to `oklch()` on emit (see
* `./color-libs.ts`). Read the maps below as a spreadsheet: one group per
* concern, light values first, dark overrides in the theme block underneath.
*/
import { instance, ref, theme } from "../shared.js";
import { oklchTransform, useColorDesignTokens, type VPPair } from "./color-libs.js";
// prettier-ignore
export const colors = useColorDesignTokens(instance, {
"accent": "#fd4b2d",
"primary": "#0066cc",
"primary.hover": "#004080",
"text": "#151515",
"text.muted": "#6a6e73",
"link": ref("color.primary"),
"link.hover": ref("color.primary.hover"),
"link.visited": "#40199a",
"surface": "#ffffff",
"surface.raised": "#fafafa",
"surface.muted": "#f0f0f0",
"border": "#8a8d90",
"border.strong": "#d2d2d2",
"border.subtle": "#f0f0f0",
"info": "#2b9af3",
"success": "#3e8635",
"warning": "#f0ab00",
"danger": "#c9190b",
});
// Dark theme overrides. Surface values are pinned near PatternFly 4's
// BackgroundColor--100 (#151515) and the drawer surface (#18191a); lighter
// surfaces visibly brighten every PF-backed dark panel.
theme("dark", (ctx) => {
const c = colors;
// prettier-ignore
const darkColors: VPPair[] = [
[c.colorText, "#e0e0e0"],
[c.colorTextMuted, "#aaabac"],
[c.colorLink, "#20a9f8"],
[c.colorLinkHover, "#73bcf7"],
[c.colorLinkVisited, "#a18fff"],
[c.colorSurface, "#121212"],
[c.colorSurfaceMuted, "#090909"],
[c.colorSurfaceRaised, "#1c1c1c"],
[c.colorBorder, "#444548"],
[c.colorBorderStrong, "#57585a"],
[c.colorBorderSubtle, "#aaabac"],
[c.colorInfo, "#73bcf7"],
[c.colorSuccess, "#5ba352"],
];
darkColors.forEach(([v, p]) => ctx.variable(v, oklchTransform(p)));
});

View File

@@ -0,0 +1,25 @@
/**
* @file Font-family tokens — the concrete brand typefaces.
*
* Names authentik's font stacks: the variable RedHat faces, then platform
* fallbacks. The semantic typography tokens (`font.family-body/heading/code`)
* alias these, and the PatternFly bridge maps `--pf-global--FontFamily--*` onto
* the semantic layer.
*
* The `@font-face` rules that bind these names to real files live in
* `@goauthentik/fonts` — this package ships no font bytes, so a consumer that
* does not load those faces falls through to the platform fallbacks.
*/
import { instance } from "../shared.js";
import { createUseVariable } from "@styleframe/theme";
const useFontFamily = createUseVariable("font-family");
export const fontFamily = useFontFamily(instance, {
"sans-serif": '"RedHatText", helvetica, arial, sans-serif',
"display": '"RedHatDisplay", helvetica, arial, sans-serif',
"monospace":
'"RedHatMono", "Liberation Mono", consolas, "SFMono-Regular", menlo, monaco, "Courier New", monospace',
});

View File

@@ -0,0 +1,16 @@
/**
* @file Re-exports every public token handle.
*
* Importing this module ensures all token modules execute their side-effects
* — registering variables and theme overrides against the singleton styleframe
* instance in `../shared.js` — before any consumer reaches `transpile()`.
*/
export * from "./color.js";
export * from "./typography.js";
export * from "./spacing.js";
export * from "./shape.js";
export * from "./fonts.js";
export * from "./shadow.js";
export * from "./motion.js";
export * from "./z-index.js";

View File

@@ -0,0 +1,34 @@
/**
* @file Motion tokens — duration and easing.
*
* Reduced motion is expressed two ways. Both override `--ak-duration-normal`
* to `0ms`:
*
* 1. `@media (prefers-reduced-motion: reduce)` — triggers automatically from
* the operating system's accessibility preference. This is the path most
* consumers actually hit.
* 2. `html[data-theme="reduced"]` — opt-in via data attribute, for explicit
* forced-reduced-motion or for previewing the reduced state. This block
* exists primarily so the styleframe DTCG export can represent reduced
* motion as a theme modifier — DTCG has no native concept for media
* queries.
*
* The two emissions are independent and don't conflict; either one alone is
* enough to zero the duration. Keeping both lets us roundtrip the reduced
* state through DTCG-aware tooling without losing the automatic OS trigger.
*/
import { media, theme, variable } from "../shared.js";
export const durationNormal = variable("duration.normal", "250ms");
export const easingStandard = variable("easing.standard", "cubic-bezier(0.645, 0.045, 0.355, 1)");
media("(prefers-reduced-motion: reduce)", (ctx) => {
ctx.selector(":root", (root) => {
root.variable(durationNormal, "0ms");
});
});
theme("reduced", (ctx) => {
ctx.variable(durationNormal, "0ms");
});

View File

@@ -0,0 +1,40 @@
/**
* @file Shadow tokens — sm..xl drop shadows and an inset variant.
*
* Dark theme uses higher opacity to read against the deeper backgrounds. The
* inset shadow uses a near-solid color in dark mode rather than a faded rgba.
*/
import { instance, theme } from "../shared.js";
import type { VPPair } from "./color-libs.js";
import { createUseVariable } from "@styleframe/theme";
const useShadowTokens = createUseVariable("shadow");
export const shadow = useShadowTokens(instance, {
sm: "0 0.0625rem 0.125rem 0 rgba(3, 3, 3, 0.12), 0 0 0.125rem 0 rgba(3, 3, 3, 0.06)",
md: "0 0.25rem 0.5rem 0rem rgba(3, 3, 3, 0.12), 0 0 0.25rem 0 rgba(3, 3, 3, 0.06)",
lg: "0 0.5rem 1rem 0 rgba(3, 3, 3, 0.16), 0 0 0.375rem 0 rgba(3, 3, 3, 0.08)",
xl: "0 0.75rem 1.5rem 0 rgba(3, 3, 3, 0.2), 0 0 0.5rem 0 rgba(3, 3, 3, 0.1)",
inset: "inset 0 0 0.625rem 0 rgba(3, 3, 3, 0.25)",
});
theme("dark", (ctx) => {
const s = shadow;
const darkShadows: VPPair[] = [
[
s.shadowSm,
"0 0.0625rem 0.125rem 0 rgba(3, 3, 3, 0.48), 0 0 0.125rem 0 rgba(3, 3, 3, 0.24)",
],
[
s.shadowMd,
"0 0.25rem 0.5rem 0rem rgba(3, 3, 3, 0.48), 0 0 0.25rem 0 rgba(3, 3, 3, 0.24)",
],
[s.shadowLg, "0 0.5rem 1rem 0 rgba(3, 3, 3, 0.64), 0 0 0.375rem 0 rgba(3, 3, 3, 0.32)"],
[s.shadowXl, "0 0.75rem 1.5rem 0 rgba(3, 3, 3, 0.8), 0 0 0.5rem 0 rgba(3, 3, 3, 0.4)"],
[s.shadowInset, "inset 0 0 0.625rem 0 #030303"],
];
darkShadows.forEach(([v, p]) => ctx.variable(v, p));
});

View File

@@ -0,0 +1,21 @@
/**
* @file Shape tokens — border radii and stroke widths.
*/
import { instance } from "../shared.js";
import { createUseVariable } from "@styleframe/theme";
const useRadii = createUseVariable("radius");
const useBorders = createUseVariable("border-width");
export const radii = useRadii(instance, {
sm: "3px",
pill: "30rem",
});
export const borders = useBorders(instance, {
sm: "1px",
md: "2px",
lg: "3px",
});

View File

@@ -0,0 +1,22 @@
/**
* @file Spacing tokens — single scale from xs (4px) to 4xl (80px) at 16px base.
*/
import { instance } from "../shared.js";
import { createUseVariable } from "@styleframe/theme";
const useSpacing = createUseVariable("space");
// 4, 8, 16, 24, 32, 48, 64, 80 pixels at the 16px base: the "standard" progression.
export const space = useSpacing(instance, {
"xs": "0.25rem",
"sm": "0.5rem",
"md": "1rem",
"lg": "1.5rem",
"xl": "2rem",
"2xl": "3rem",
"3xl": "4rem",
"4xl": "5rem",
});

View File

@@ -0,0 +1,46 @@
/**
* @file Typography tokens — font families, sizes, line heights, weights.
*
* The semantic family tokens (body/heading/code) alias the concrete brand
* stacks declared in `./fonts.ts` (`--ak-font-family-sans-serif/display/
* monospace`), so the package resolves fonts on its own. Sizes follow a
* modular xs..4xl scale. `semi-bold` is deliberately omitted from the public
* surface because
* PatternFly's `--pf-global--FontWeight--semi-bold` collapses to 700 (same as
* bold) unless the Overpass font scale is active.
*/
import { instance, variable } from "../shared.js";
import { useFontSizeDesignTokens } from "@styleframe/theme";
export const fontFamilyBody = variable(
"font-family.body",
"var(--ak-global--font-family--sans-serif)",
);
export const fontFamilyHeading = variable(
"font-family.heading",
"var(--ak-global--font-family--display)",
);
export const fontFamilyCode = variable(
"font-family.code",
"var(--ak-global--font-family--monospace)",
);
export const fontSize = useFontSizeDesignTokens(instance, {
"xs": "0.75rem",
"sm": "0.875rem",
"md": "1rem",
"lg": "1.125rem",
"xl": "1.25rem",
"2xl": "1.5rem",
"3xl": "1.75rem",
"4xl": "2.25rem",
});
export const fontWeightLight = variable("font-weight.light", 300);
export const fontWeightNormal = variable("font-weight.normal", 400);
export const fontWeightBold = variable("font-weight.bold", 700);
export const lineHeightSm = variable("line-height.sm", 1.3);
export const lineHeightMd = variable("line-height.md", 1.5);

View File

@@ -0,0 +1,22 @@
/**
* @file Z-index tokens — xs..2xl tiers matching PatternFly 4's scale.
*
* Spelled out as `z-index.*` rather than `z.*` so the emitted CSS
* (`--ak-z-index-md`) is unambiguous in brand custom CSS and IDE
* autocomplete.
*/
import { instance } from "../shared.js";
import { createUseVariable } from "@styleframe/theme";
const useZIndex = createUseVariable("z-index");
export const zIndex = useZIndex(instance, {
"xs": 100,
"sm": 200,
"md": 300,
"lg": 400,
"xl": 500,
"2xl": 600,
});

View File

@@ -0,0 +1,18 @@
/**
* @file Styleframe CLI entry point.
*
* The styleframe CLI (used by `pnpm run build:dtcg`) loads this file via jiti
* and expects a default export that is a configured {@link Styleframe} instance
* with all variables/themes already registered.
*
* Importing `./src/tokens/index.js` triggers the side-effects that register
* every token against the shared instance; we re-export `instance` as the
* default export so the CLI's `dtcg export` and `build` commands operate on
* the same tree the rest of the package uses.
*/
import "./src/tokens/index.js";
import { instance } from "./src/shared.js";
export default instance;

View File

@@ -0,0 +1,14 @@
{
"extends": "@goauthentik/tsconfig",
"compilerOptions": {
"lib": ["ESNext"],
"composite": false,
"incremental": false,
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "./src",
"checkJs": true
},
"include": ["./src/**/*"],
"exclude": ["**/out/**/*"]
}

3989
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@ packages:
- "packages/geo" - "packages/geo"
- "packages/logger-js" - "packages/logger-js"
- "packages/prettier-config" - "packages/prettier-config"
- "packages/theme"
- "packages/tsconfig" - "packages/tsconfig"
- "lifecycle/aws" - "lifecycle/aws"

View File

@@ -103,6 +103,7 @@
"@goauthentik/eslint-config": "^2.0.3", "@goauthentik/eslint-config": "^2.0.3",
"@goauthentik/fonts": "link:../packages/fonts", "@goauthentik/fonts": "link:../packages/fonts",
"@goauthentik/prettier-config": "^3.5.0", "@goauthentik/prettier-config": "^3.5.0",
"@goauthentik/theme": "link:../packages/theme",
"@goauthentik/tsconfig": "^1.0.9", "@goauthentik/tsconfig": "^1.0.9",
"@hcaptcha/types": "^1.2.0", "@hcaptcha/types": "^1.2.0",
"@lit/context": "^1.1.6", "@lit/context": "^1.1.6",

380
web/pnpm-lock.yaml generated
View File

@@ -32,7 +32,7 @@ catalogs:
version: 4.1.10 version: 4.1.10
esbuild: esbuild:
specifier: ^0.28.1 specifier: ^0.28.1
version: 0.28.1 version: 0.28.2
eslint: eslint:
specifier: ^9.39.5 specifier: ^9.39.5
version: 9.39.5 version: 9.39.5
@@ -120,7 +120,7 @@ importers:
version: link:packages/core version: link:packages/core
'@goauthentik/esbuild-plugin-live-reload': '@goauthentik/esbuild-plugin-live-reload':
specifier: ^2.0.3 specifier: ^2.0.3
version: 2.0.3(esbuild@0.28.1) version: 2.0.3(esbuild@0.28.2)
'@goauthentik/eslint-config': '@goauthentik/eslint-config':
specifier: ^2.0.3 specifier: ^2.0.3
version: 2.0.3(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(jiti@2.7.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(typescript-eslint@8.65.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(typescript@6.0.3) version: 2.0.3(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(jiti@2.7.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(typescript-eslint@8.65.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(typescript@6.0.3)
@@ -130,6 +130,9 @@ importers:
'@goauthentik/prettier-config': '@goauthentik/prettier-config':
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.5.0(jiti@2.7.0)(prettier-plugin-packagejson@3.0.2(prettier@3.8.3))(prettier@3.8.3)(supports-color@10.2.2) version: 3.5.0(jiti@2.7.0)(prettier-plugin-packagejson@3.0.2(prettier@3.8.3))(prettier@3.8.3)(supports-color@10.2.2)
'@goauthentik/theme':
specifier: link:../packages/theme
version: link:../packages/theme
'@goauthentik/tsconfig': '@goauthentik/tsconfig':
specifier: ^1.0.9 specifier: ^1.0.9
version: 1.0.9 version: 1.0.9
@@ -180,7 +183,7 @@ importers:
version: 10.69.0 version: 10.69.0
'@storybook/addon-docs': '@storybook/addon-docs':
specifier: ^10.5.7 specifier: ^10.5.7
version: 10.5.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) version: 10.5.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
'@storybook/addon-links': '@storybook/addon-links':
specifier: ^10.5.7 specifier: ^10.5.7
version: 10.5.7(@types/react@19.2.17)(react@19.2.8)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8)) version: 10.5.7(@types/react@19.2.17)(react@19.2.8)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))
@@ -189,7 +192,7 @@ importers:
version: 10.5.7(lit@3.3.3)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8)) version: 10.5.7(lit@3.3.3)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))
'@storybook/web-components-vite': '@storybook/web-components-vite':
specifier: ^10.5.7 specifier: ^10.5.7
version: 10.5.7(esbuild@0.28.1)(lit@3.3.3)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) version: 10.5.7(esbuild@0.28.2)(lit@3.3.3)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
'@types/codemirror': '@types/codemirror':
specifier: ^5.60.17 specifier: ^5.60.17
version: 5.60.17 version: 5.60.17
@@ -222,10 +225,10 @@ importers:
version: 7.0.0-dev.20260707.2 version: 7.0.0-dev.20260707.2
'@vitest/browser': '@vitest/browser':
specifier: 'catalog:' specifier: 'catalog:'
version: 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10) version: 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10)
'@vitest/browser-playwright': '@vitest/browser-playwright':
specifier: 'catalog:' specifier: 'catalog:'
version: 4.1.10(playwright@1.62.1)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10) version: 4.1.10(playwright@1.62.1)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10)
'@webcomponents/webcomponentsjs': '@webcomponents/webcomponentsjs':
specifier: ^2.8.0 specifier: ^2.8.0
version: 2.8.0 version: 2.8.0
@@ -261,7 +264,7 @@ importers:
version: 3.4.13 version: 3.4.13
esbuild: esbuild:
specifier: 'catalog:' specifier: 'catalog:'
version: 0.28.1 version: 0.28.2
eslint: eslint:
specifier: 'catalog:' specifier: 'catalog:'
version: 9.39.5(jiti@2.7.0)(supports-color@10.2.2) version: 9.39.5(jiti@2.7.0)(supports-color@10.2.2)
@@ -405,10 +408,10 @@ importers:
version: 5.1.0 version: 5.1.0
vite: vite:
specifier: 'catalog:' specifier: 'catalog:'
version: 8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) version: 8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)
vitest: vitest:
specifier: 'catalog:' specifier: 'catalog:'
version: 4.1.10(@types/node@26.1.2)(@vitest/browser-playwright@4.1.10)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) version: 4.1.10(@types/node@26.1.2)(@vitest/browser-playwright@4.1.10)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
webcomponent-qr-code: webcomponent-qr-code:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0 version: 2.0.0
@@ -782,48 +785,24 @@ packages:
'@emnapi/wasi-threads@2.0.1': '@emnapi/wasi-threads@2.0.1':
resolution: {integrity: sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==} resolution: {integrity: sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==}
'@esbuild/aix-ppc64@0.28.1':
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/aix-ppc64@0.28.2': '@esbuild/aix-ppc64@0.28.2':
resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ppc64] cpu: [ppc64]
os: [aix] os: [aix]
'@esbuild/android-arm64@0.28.1':
resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.28.2': '@esbuild/android-arm64@0.28.2':
resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
'@esbuild/android-arm@0.28.1':
resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.28.2': '@esbuild/android-arm@0.28.2':
resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm] cpu: [arm]
os: [android] os: [android]
'@esbuild/android-x64@0.28.1':
resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.28.2': '@esbuild/android-x64@0.28.2':
resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -842,36 +821,18 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@esbuild/darwin-x64@0.28.1':
resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.28.2': '@esbuild/darwin-x64@0.28.2':
resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@esbuild/freebsd-arm64@0.28.1':
resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.28.2': '@esbuild/freebsd-arm64@0.28.2':
resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [freebsd] os: [freebsd]
'@esbuild/freebsd-x64@0.28.1':
resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.28.2': '@esbuild/freebsd-x64@0.28.2':
resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -890,84 +851,42 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@esbuild/linux-arm@0.28.1':
resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.28.2': '@esbuild/linux-arm@0.28.2':
resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@esbuild/linux-ia32@0.28.1':
resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.28.2': '@esbuild/linux-ia32@0.28.2':
resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ia32] cpu: [ia32]
os: [linux] os: [linux]
'@esbuild/linux-loong64@0.28.1':
resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.28.2': '@esbuild/linux-loong64@0.28.2':
resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
'@esbuild/linux-mips64el@0.28.1':
resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.28.2': '@esbuild/linux-mips64el@0.28.2':
resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [mips64el] cpu: [mips64el]
os: [linux] os: [linux]
'@esbuild/linux-ppc64@0.28.1':
resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.28.2': '@esbuild/linux-ppc64@0.28.2':
resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
'@esbuild/linux-riscv64@0.28.1':
resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.28.2': '@esbuild/linux-riscv64@0.28.2':
resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
'@esbuild/linux-s390x@0.28.1':
resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.28.2': '@esbuild/linux-s390x@0.28.2':
resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -986,108 +905,54 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@esbuild/netbsd-arm64@0.28.1':
resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-arm64@0.28.2': '@esbuild/netbsd-arm64@0.28.2':
resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [netbsd] os: [netbsd]
'@esbuild/netbsd-x64@0.28.1':
resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.28.2': '@esbuild/netbsd-x64@0.28.2':
resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [netbsd] os: [netbsd]
'@esbuild/openbsd-arm64@0.28.1':
resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-arm64@0.28.2': '@esbuild/openbsd-arm64@0.28.2':
resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [openbsd] os: [openbsd]
'@esbuild/openbsd-x64@0.28.1':
resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.28.2': '@esbuild/openbsd-x64@0.28.2':
resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [openbsd] os: [openbsd]
'@esbuild/openharmony-arm64@0.28.1':
resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/openharmony-arm64@0.28.2': '@esbuild/openharmony-arm64@0.28.2':
resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [openharmony] os: [openharmony]
'@esbuild/sunos-x64@0.28.1':
resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.28.2': '@esbuild/sunos-x64@0.28.2':
resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [sunos] os: [sunos]
'@esbuild/win32-arm64@0.28.1':
resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.28.2': '@esbuild/win32-arm64@0.28.2':
resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@esbuild/win32-ia32@0.28.1':
resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.28.2': '@esbuild/win32-ia32@0.28.2':
resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
'@esbuild/win32-x64@0.28.1':
resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.28.2': '@esbuild/win32-x64@0.28.2':
resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -3770,11 +3635,6 @@ packages:
esast-util-from-js@2.0.1: esast-util-from-js@2.0.1:
resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==}
esbuild@0.28.1:
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
engines: {node: '>=18'}
hasBin: true
esbuild@0.28.2: esbuild@0.28.2:
resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -5132,10 +4992,6 @@ packages:
resolution: {integrity: sha512-bjdr2xW1dBCMsMGGsUeqM4eFI60m94+szhxWys+B1ztIt6gWSfeGBdSVCIawezeHYLYn0j6zrsXdQS/JllBzww==} resolution: {integrity: sha512-bjdr2xW1dBCMsMGGsUeqM4eFI60m94+szhxWys+B1ztIt6gWSfeGBdSVCIawezeHYLYn0j6zrsXdQS/JllBzww==}
engines: {node: '>=6'} engines: {node: '>=6'}
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
minimatch@10.2.6: minimatch@10.2.6:
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
engines: {node: 18 || 20 || >=22} engines: {node: 18 || 20 || >=22}
@@ -6156,10 +6012,6 @@ packages:
tinybench@2.9.0: tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
tinyexec@1.2.4:
resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
engines: {node: '>=18'}
tinyexec@1.3.0: tinyexec@1.3.0:
resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -6603,18 +6455,6 @@ packages:
resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==} resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==}
engines: {node: ^20.17.0 || >=22.9.0} engines: {node: ^20.17.0 || >=22.9.0}
ws@8.21.0:
resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
ws@8.21.3: ws@8.21.3:
resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@@ -7021,27 +6861,15 @@ snapshots:
tslib: 2.8.1 tslib: 2.8.1
optional: true optional: true
'@esbuild/aix-ppc64@0.28.1':
optional: true
'@esbuild/aix-ppc64@0.28.2': '@esbuild/aix-ppc64@0.28.2':
optional: true optional: true
'@esbuild/android-arm64@0.28.1':
optional: true
'@esbuild/android-arm64@0.28.2': '@esbuild/android-arm64@0.28.2':
optional: true optional: true
'@esbuild/android-arm@0.28.1':
optional: true
'@esbuild/android-arm@0.28.2': '@esbuild/android-arm@0.28.2':
optional: true optional: true
'@esbuild/android-x64@0.28.1':
optional: true
'@esbuild/android-x64@0.28.2': '@esbuild/android-x64@0.28.2':
optional: true optional: true
@@ -7051,21 +6879,12 @@ snapshots:
'@esbuild/darwin-arm64@0.28.2': '@esbuild/darwin-arm64@0.28.2':
optional: true optional: true
'@esbuild/darwin-x64@0.28.1':
optional: true
'@esbuild/darwin-x64@0.28.2': '@esbuild/darwin-x64@0.28.2':
optional: true optional: true
'@esbuild/freebsd-arm64@0.28.1':
optional: true
'@esbuild/freebsd-arm64@0.28.2': '@esbuild/freebsd-arm64@0.28.2':
optional: true optional: true
'@esbuild/freebsd-x64@0.28.1':
optional: true
'@esbuild/freebsd-x64@0.28.2': '@esbuild/freebsd-x64@0.28.2':
optional: true optional: true
@@ -7075,45 +6894,24 @@ snapshots:
'@esbuild/linux-arm64@0.28.2': '@esbuild/linux-arm64@0.28.2':
optional: true optional: true
'@esbuild/linux-arm@0.28.1':
optional: true
'@esbuild/linux-arm@0.28.2': '@esbuild/linux-arm@0.28.2':
optional: true optional: true
'@esbuild/linux-ia32@0.28.1':
optional: true
'@esbuild/linux-ia32@0.28.2': '@esbuild/linux-ia32@0.28.2':
optional: true optional: true
'@esbuild/linux-loong64@0.28.1':
optional: true
'@esbuild/linux-loong64@0.28.2': '@esbuild/linux-loong64@0.28.2':
optional: true optional: true
'@esbuild/linux-mips64el@0.28.1':
optional: true
'@esbuild/linux-mips64el@0.28.2': '@esbuild/linux-mips64el@0.28.2':
optional: true optional: true
'@esbuild/linux-ppc64@0.28.1':
optional: true
'@esbuild/linux-ppc64@0.28.2': '@esbuild/linux-ppc64@0.28.2':
optional: true optional: true
'@esbuild/linux-riscv64@0.28.1':
optional: true
'@esbuild/linux-riscv64@0.28.2': '@esbuild/linux-riscv64@0.28.2':
optional: true optional: true
'@esbuild/linux-s390x@0.28.1':
optional: true
'@esbuild/linux-s390x@0.28.2': '@esbuild/linux-s390x@0.28.2':
optional: true optional: true
@@ -7123,57 +6921,30 @@ snapshots:
'@esbuild/linux-x64@0.28.2': '@esbuild/linux-x64@0.28.2':
optional: true optional: true
'@esbuild/netbsd-arm64@0.28.1':
optional: true
'@esbuild/netbsd-arm64@0.28.2': '@esbuild/netbsd-arm64@0.28.2':
optional: true optional: true
'@esbuild/netbsd-x64@0.28.1':
optional: true
'@esbuild/netbsd-x64@0.28.2': '@esbuild/netbsd-x64@0.28.2':
optional: true optional: true
'@esbuild/openbsd-arm64@0.28.1':
optional: true
'@esbuild/openbsd-arm64@0.28.2': '@esbuild/openbsd-arm64@0.28.2':
optional: true optional: true
'@esbuild/openbsd-x64@0.28.1':
optional: true
'@esbuild/openbsd-x64@0.28.2': '@esbuild/openbsd-x64@0.28.2':
optional: true optional: true
'@esbuild/openharmony-arm64@0.28.1':
optional: true
'@esbuild/openharmony-arm64@0.28.2': '@esbuild/openharmony-arm64@0.28.2':
optional: true optional: true
'@esbuild/sunos-x64@0.28.1':
optional: true
'@esbuild/sunos-x64@0.28.2': '@esbuild/sunos-x64@0.28.2':
optional: true optional: true
'@esbuild/win32-arm64@0.28.1':
optional: true
'@esbuild/win32-arm64@0.28.2': '@esbuild/win32-arm64@0.28.2':
optional: true optional: true
'@esbuild/win32-ia32@0.28.1':
optional: true
'@esbuild/win32-ia32@0.28.2': '@esbuild/win32-ia32@0.28.2':
optional: true optional: true
'@esbuild/win32-x64@0.28.1':
optional: true
'@esbuild/win32-x64@0.28.2': '@esbuild/win32-x64@0.28.2':
optional: true optional: true
@@ -7239,9 +7010,9 @@ snapshots:
'@goauthentik/brand-assets@2.0.0': {} '@goauthentik/brand-assets@2.0.0': {}
'@goauthentik/esbuild-plugin-live-reload@2.0.3(esbuild@0.28.1)': '@goauthentik/esbuild-plugin-live-reload@2.0.3(esbuild@0.28.2)':
dependencies: dependencies:
esbuild: 0.28.1 esbuild: 0.28.2
find-free-ports: 3.1.1 find-free-ports: 3.1.1
'@goauthentik/eslint-config@2.0.3(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(jiti@2.7.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(typescript-eslint@8.65.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(typescript@6.0.3)': '@goauthentik/eslint-config@2.0.3(@typescript-eslint/parser@8.65.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(jiti@2.7.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(supports-color@10.2.2)(typescript-eslint@8.65.0(eslint@9.39.5(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(typescript@6.0.3)':
@@ -7580,13 +7351,6 @@ snapshots:
'@tybys/wasm-util': 0.10.3 '@tybys/wasm-util': 0.10.3
optional: true optional: true
'@napi-rs/wasm-runtime@1.2.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)':
dependencies:
'@emnapi/core': 2.0.0-alpha.3
'@emnapi/runtime': 2.0.0-alpha.3
'@tybys/wasm-util': 0.10.3
optional: true
'@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)':
dependencies: dependencies:
'@emnapi/core': 1.11.2 '@emnapi/core': 1.11.2
@@ -7601,6 +7365,13 @@ snapshots:
'@tybys/wasm-util': 0.10.3 '@tybys/wasm-util': 0.10.3
optional: true optional: true
'@napi-rs/wasm-runtime@1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)':
dependencies:
'@emnapi/core': 2.0.0-alpha.3
'@emnapi/runtime': 2.0.0-alpha.3
'@tybys/wasm-util': 0.10.3
optional: true
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
dependencies: dependencies:
'@nodelib/fs.stat': 2.0.5 '@nodelib/fs.stat': 2.0.5
@@ -7883,7 +7654,7 @@ snapshots:
dependencies: dependencies:
'@emnapi/core': 2.0.0-alpha.3 '@emnapi/core': 2.0.0-alpha.3
'@emnapi/runtime': 2.0.0-alpha.3 '@emnapi/runtime': 2.0.0-alpha.3
'@napi-rs/wasm-runtime': 1.2.1(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)
optional: true optional: true
'@rolldown/binding-win32-arm64-msvc@1.2.1': '@rolldown/binding-win32-arm64-msvc@1.2.1':
@@ -7928,7 +7699,7 @@ snapshots:
dependencies: dependencies:
'@types/estree': 1.0.9 '@types/estree': 1.0.9
estree-walker: 2.0.2 estree-walker: 2.0.2
picomatch: 4.0.4 picomatch: 4.0.5
optionalDependencies: optionalDependencies:
rollup: 4.62.4 rollup: 4.62.4
@@ -8053,10 +7824,10 @@ snapshots:
'@standard-schema/spec@1.1.0': {} '@standard-schema/spec@1.1.0': {}
'@storybook/addon-docs@10.5.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': '@storybook/addon-docs@10.5.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))':
dependencies: dependencies:
'@mdx-js/react': 3.1.1(@types/react@19.2.17)(react@19.2.8) '@mdx-js/react': 3.1.1(@types/react@19.2.17)(react@19.2.8)
'@storybook/csf-plugin': 10.5.7(esbuild@0.28.1)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@storybook/csf-plugin': 10.5.7(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
'@storybook/icons': 2.1.0(react@19.2.8) '@storybook/icons': 2.1.0(react@19.2.8)
'@storybook/react-dom-shim': 10.5.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8)) '@storybook/react-dom-shim': 10.5.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))
react: 19.2.8 react: 19.2.8
@@ -8080,25 +7851,25 @@ snapshots:
'@types/react': 19.2.17 '@types/react': 19.2.17
react: 19.2.8 react: 19.2.8
'@storybook/builder-vite@10.5.7(esbuild@0.28.1)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': '@storybook/builder-vite@10.5.7(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))':
dependencies: dependencies:
'@storybook/csf-plugin': 10.5.7(esbuild@0.28.1)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@storybook/csf-plugin': 10.5.7(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
storybook: 10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8) storybook: 10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8)
ts-dedent: 2.3.0 ts-dedent: 2.3.0
vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)
transitivePeerDependencies: transitivePeerDependencies:
- esbuild - esbuild
- rollup - rollup
- webpack - webpack
'@storybook/csf-plugin@10.5.7(esbuild@0.28.1)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': '@storybook/csf-plugin@10.5.7(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))':
dependencies: dependencies:
storybook: 10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8) storybook: 10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8)
unplugin: 2.3.11 unplugin: 2.3.11
optionalDependencies: optionalDependencies:
esbuild: 0.28.1 esbuild: 0.28.2
rollup: 4.62.4 rollup: 4.62.4
vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)
'@storybook/global@5.0.0': {} '@storybook/global@5.0.0': {}
@@ -8115,12 +7886,12 @@ snapshots:
'@types/react': 19.2.17 '@types/react': 19.2.17
'@types/react-dom': 19.2.3(@types/react@19.2.17) '@types/react-dom': 19.2.3(@types/react@19.2.17)
'@storybook/web-components-vite@10.5.7(esbuild@0.28.1)(lit@3.3.3)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': '@storybook/web-components-vite@10.5.7(esbuild@0.28.2)(lit@3.3.3)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))':
dependencies: dependencies:
'@storybook/builder-vite': 10.5.7(esbuild@0.28.1)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@storybook/builder-vite': 10.5.7(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
'@storybook/web-components': 10.5.7(lit@3.3.3)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8)) '@storybook/web-components': 10.5.7(lit@3.3.3)(storybook@10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8))
storybook: 10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8) storybook: 10.5.7(@types/react@19.2.17)(prettier@3.8.3)(react@19.2.8)
vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)
transitivePeerDependencies: transitivePeerDependencies:
- esbuild - esbuild
- lit - lit
@@ -9014,30 +8785,30 @@ snapshots:
d3-selection: 3.0.0 d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0) d3-transition: 3.0.1(d3-selection@3.0.0)
'@vitest/browser-playwright@4.1.10(playwright@1.62.1)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10)': '@vitest/browser-playwright@4.1.10(playwright@1.62.1)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10)':
dependencies: dependencies:
'@vitest/browser': 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10) '@vitest/browser': 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10)
'@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
playwright: 1.62.1 playwright: 1.62.1
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
vitest: 4.1.10(@types/node@26.1.2)(@vitest/browser-playwright@4.1.10)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) vitest: 4.1.10(@types/node@26.1.2)(@vitest/browser-playwright@4.1.10)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- msw - msw
- utf-8-validate - utf-8-validate
- vite - vite
'@vitest/browser@4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10)': '@vitest/browser@4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10)':
dependencies: dependencies:
'@blazediff/core': 1.9.1 '@blazediff/core': 1.9.1
'@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
'@vitest/utils': 4.1.10 '@vitest/utils': 4.1.10
magic-string: 0.30.21 magic-string: 0.30.21
pngjs: 7.0.0 pngjs: 7.0.0
sirv: 3.0.2 sirv: 3.0.2
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
vitest: 4.1.10(@types/node@26.1.2)(@vitest/browser-playwright@4.1.10)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) vitest: 4.1.10(@types/node@26.1.2)(@vitest/browser-playwright@4.1.10)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
ws: 8.21.0 ws: 8.21.3
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- msw - msw
@@ -9061,13 +8832,13 @@ snapshots:
chai: 6.2.2 chai: 6.2.2
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
'@vitest/mocker@4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))': '@vitest/mocker@4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))':
dependencies: dependencies:
'@vitest/spy': 4.1.10 '@vitest/spy': 4.1.10
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.21 magic-string: 0.30.21
optionalDependencies: optionalDependencies:
vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)
'@vitest/pretty-format@3.2.4': '@vitest/pretty-format@3.2.4':
dependencies: dependencies:
@@ -9254,6 +9025,10 @@ snapshots:
dependencies: dependencies:
acorn: 8.17.0 acorn: 8.17.0
acorn-jsx@5.3.2(acorn@8.18.0):
dependencies:
acorn: 8.18.0
acorn@8.17.0: {} acorn@8.17.0: {}
acorn@8.18.0: {} acorn@8.18.0: {}
@@ -10147,39 +9922,10 @@ snapshots:
esast-util-from-js@2.0.1: esast-util-from-js@2.0.1:
dependencies: dependencies:
'@types/estree-jsx': 1.0.5 '@types/estree-jsx': 1.0.5
acorn: 8.17.0 acorn: 8.18.0
esast-util-from-estree: 2.0.0 esast-util-from-estree: 2.0.0
vfile-message: 4.0.3 vfile-message: 4.0.3
esbuild@0.28.1:
optionalDependencies:
'@esbuild/aix-ppc64': 0.28.1
'@esbuild/android-arm': 0.28.1
'@esbuild/android-arm64': 0.28.1
'@esbuild/android-x64': 0.28.1
'@esbuild/darwin-arm64': 0.28.1
'@esbuild/darwin-x64': 0.28.1
'@esbuild/freebsd-arm64': 0.28.1
'@esbuild/freebsd-x64': 0.28.1
'@esbuild/linux-arm': 0.28.1
'@esbuild/linux-arm64': 0.28.1
'@esbuild/linux-ia32': 0.28.1
'@esbuild/linux-loong64': 0.28.1
'@esbuild/linux-mips64el': 0.28.1
'@esbuild/linux-ppc64': 0.28.1
'@esbuild/linux-riscv64': 0.28.1
'@esbuild/linux-s390x': 0.28.1
'@esbuild/linux-x64': 0.28.1
'@esbuild/netbsd-arm64': 0.28.1
'@esbuild/netbsd-x64': 0.28.1
'@esbuild/openbsd-arm64': 0.28.1
'@esbuild/openbsd-x64': 0.28.1
'@esbuild/openharmony-arm64': 0.28.1
'@esbuild/sunos-x64': 0.28.1
'@esbuild/win32-arm64': 0.28.1
'@esbuild/win32-ia32': 0.28.1
'@esbuild/win32-x64': 0.28.1
esbuild@0.28.2: esbuild@0.28.2:
optionalDependencies: optionalDependencies:
'@esbuild/aix-ppc64': 0.28.2 '@esbuild/aix-ppc64': 0.28.2
@@ -10363,8 +10109,8 @@ snapshots:
espree@10.4.0: espree@10.4.0:
dependencies: dependencies:
acorn: 8.17.0 acorn: 8.18.0
acorn-jsx: 5.3.2(acorn@8.17.0) acorn-jsx: 5.3.2(acorn@8.18.0)
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
esprima@4.0.1: {} esprima@4.0.1: {}
@@ -10602,7 +10348,7 @@ snapshots:
immutable: 5.1.9 immutable: 5.1.9
is-builtin-module: 3.2.1 is-builtin-module: 3.2.1
log4js: 6.9.1(supports-color@10.2.2) log4js: 6.9.1(supports-color@10.2.2)
minimatch: 10.2.5 minimatch: 10.2.6
node-cache: 5.1.2 node-cache: 5.1.2
optionator: 0.9.4 optionator: 0.9.4
prettier: 3.8.3 prettier: 3.8.3
@@ -12012,10 +11758,6 @@ snapshots:
dependencies: dependencies:
lodash: 4.18.1 lodash: 4.18.1
minimatch@10.2.5:
dependencies:
brace-expansion: 5.0.7
minimatch@10.2.6: minimatch@10.2.6:
dependencies: dependencies:
brace-expansion: 5.0.9 brace-expansion: 5.0.9
@@ -13334,8 +13076,6 @@ snapshots:
tinybench@2.9.0: {} tinybench@2.9.0: {}
tinyexec@1.2.4: {}
tinyexec@1.3.0: {} tinyexec@1.3.0: {}
tinyglobby@0.2.17: tinyglobby@0.2.17:
@@ -13616,7 +13356,7 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
vfile-message: 4.0.3 vfile-message: 4.0.3
vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0): vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0):
dependencies: dependencies:
lightningcss: 1.33.0 lightningcss: 1.33.0
picomatch: 4.0.5 picomatch: 4.0.5
@@ -13625,15 +13365,15 @@ snapshots:
tinyglobby: 0.2.17 tinyglobby: 0.2.17
optionalDependencies: optionalDependencies:
'@types/node': 26.1.2 '@types/node': 26.1.2
esbuild: 0.28.1 esbuild: 0.28.2
fsevents: 2.3.3 fsevents: 2.3.3
jiti: 2.7.0 jiti: 2.7.0
yaml: 2.9.0 yaml: 2.9.0
vitest@4.1.10(@types/node@26.1.2)(@vitest/browser-playwright@4.1.10)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)): vitest@4.1.10(@types/node@26.1.2)(@vitest/browser-playwright@4.1.10)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)):
dependencies: dependencies:
'@vitest/expect': 4.1.10 '@vitest/expect': 4.1.10
'@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))
'@vitest/pretty-format': 4.1.10 '@vitest/pretty-format': 4.1.10
'@vitest/runner': 4.1.10 '@vitest/runner': 4.1.10
'@vitest/snapshot': 4.1.10 '@vitest/snapshot': 4.1.10
@@ -13647,14 +13387,14 @@ snapshots:
picomatch: 4.0.5 picomatch: 4.0.5
std-env: 4.2.0 std-env: 4.2.0
tinybench: 2.9.0 tinybench: 2.9.0
tinyexec: 1.2.4 tinyexec: 1.3.0
tinyglobby: 0.2.17 tinyglobby: 0.2.17
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0) vite: 8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 26.1.2 '@types/node': 26.1.2
'@vitest/browser-playwright': 4.1.10(playwright@1.62.1)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10) '@vitest/browser-playwright': 4.1.10(playwright@1.62.1)(vite@8.2.0(@types/node@26.1.2)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.10)
transitivePeerDependencies: transitivePeerDependencies:
- msw - msw
@@ -13782,8 +13522,6 @@ snapshots:
dependencies: dependencies:
signal-exit: 4.1.0 signal-exit: 4.1.0
ws@8.21.0: {}
ws@8.21.3: {} ws@8.21.3: {}
wsl-utils@0.1.0: wsl-utils@0.1.0:

View File

@@ -13,6 +13,7 @@ packages:
# `pnpm install` prunes right back out. # `pnpm install` prunes right back out.
- "!packages/fonts" - "!packages/fonts"
- "!packages/prettier-config" - "!packages/prettier-config"
- "!packages/theme"
- "!packages/tsconfig" - "!packages/tsconfig"
# pnpm's default isolated linker exposes packages strictly — only declared # pnpm's default isolated linker exposes packages strictly — only declared

View File

@@ -122,6 +122,12 @@ const BASE_ESBUILD_OPTIONS = {
/** /**
* Conditions for module resolution. * Conditions for module resolution.
* *
* `bundler` lets workspace packages that ship TypeScript sources (e.g.
* `@goauthentik/theme`) expose `src/*.ts` to this build while keeping a
* compiled `default` entry for plain Node consumers. esbuild does not
* imply it, so without this the resolver falls through to the built
* output and every edit needs a rebuild of the dependency first.
*
* @see https://esbuild.github.io/api/#conditions * @see https://esbuild.github.io/api/#conditions
* @see https://nodejs.org/api/packages.html#packages_conditional_exports * @see https://nodejs.org/api/packages.html#packages_conditional_exports
*/ */