From 2e6d7ece1c6a18a3b01e459e0dc5474d7c5b4fa5 Mon Sep 17 00:00:00 2001 From: jplanckeel Date: Sat, 31 Oct 2026 14:36:31 +0100 Subject: [PATCH 1/8] docs: add the Tracker design system (Surligneur) Brand book, tokens, component specs, reference implementation, page mockups, logos and fonts, plus the implementation plan in five batches. Co-Authored-By: Claude Opus 5.5 --- docs/design-system/IMPLEMENTATION.md | 125 ++++ docs/design-system/README.md | 115 ++++ docs/design-system/assets/logos/README.md | 10 + .../assets/logos/tracker-lockup-inverse.svg | 1 + .../assets/logos/tracker-lockup.svg | 1 + .../assets/logos/tracker-mark-inverse.svg | 1 + .../assets/logos/tracker-mark.svg | 1 + docs/design-system/components/Button.md | 10 + .../components/CommandPalette.md | 10 + docs/design-system/components/EventDrawer.md | 24 + docs/design-system/components/EventRow.md | 14 + docs/design-system/components/FilterBar.md | 8 + docs/design-system/components/Highlight.md | 9 + docs/design-system/components/Icon.md | 10 + docs/design-system/components/ImpactMark.md | 21 + docs/design-system/components/Kbd.md | 5 + docs/design-system/components/Logo.md | 10 + docs/design-system/components/MetricStrip.md | 10 + .../components/SegmentedControl.md | 5 + docs/design-system/components/Sidebar.md | 9 + docs/design-system/components/StatusMark.md | 23 + docs/design-system/components/TimeRuler.md | 9 + docs/design-system/fonts/OFL-SplineSans.txt | 93 +++ .../fonts/OFL-SplineSansMono.txt | 93 +++ docs/design-system/fonts/SplineSans.ttf | Bin 0 -> 146896 bytes docs/design-system/fonts/SplineSansMono.ttf | Bin 0 -> 118744 bytes docs/design-system/mockups/README.md | 21 + .../mockups/architecture.dc.html | 491 +++++++++++++ .../design-system/mockups/compiled-tokens.css | 37 + docs/design-system/mockups/dashboard.dc.html | 374 ++++++++++ docs/design-system/mockups/insights.dc.html | 321 +++++++++ .../mockups/search-and-filters.dc.html | 132 ++++ docs/design-system/mockups/streamline.dc.html | 650 ++++++++++++++++++ docs/design-system/reference/bundle.css | 197 ++++++ docs/design-system/reference/bundle.js | 433 ++++++++++++ docs/design-system/reference/index.d.ts | 59 ++ docs/design-system/tokens.json | 138 ++++ 37 files changed, 3470 insertions(+) create mode 100644 docs/design-system/IMPLEMENTATION.md create mode 100644 docs/design-system/README.md create mode 100644 docs/design-system/assets/logos/README.md create mode 100644 docs/design-system/assets/logos/tracker-lockup-inverse.svg create mode 100644 docs/design-system/assets/logos/tracker-lockup.svg create mode 100644 docs/design-system/assets/logos/tracker-mark-inverse.svg create mode 100644 docs/design-system/assets/logos/tracker-mark.svg create mode 100644 docs/design-system/components/Button.md create mode 100644 docs/design-system/components/CommandPalette.md create mode 100644 docs/design-system/components/EventDrawer.md create mode 100644 docs/design-system/components/EventRow.md create mode 100644 docs/design-system/components/FilterBar.md create mode 100644 docs/design-system/components/Highlight.md create mode 100644 docs/design-system/components/Icon.md create mode 100644 docs/design-system/components/ImpactMark.md create mode 100644 docs/design-system/components/Kbd.md create mode 100644 docs/design-system/components/Logo.md create mode 100644 docs/design-system/components/MetricStrip.md create mode 100644 docs/design-system/components/SegmentedControl.md create mode 100644 docs/design-system/components/Sidebar.md create mode 100644 docs/design-system/components/StatusMark.md create mode 100644 docs/design-system/components/TimeRuler.md create mode 100644 docs/design-system/fonts/OFL-SplineSans.txt create mode 100644 docs/design-system/fonts/OFL-SplineSansMono.txt create mode 100644 docs/design-system/fonts/SplineSans.ttf create mode 100644 docs/design-system/fonts/SplineSansMono.ttf create mode 100644 docs/design-system/mockups/README.md create mode 100644 docs/design-system/mockups/architecture.dc.html create mode 100644 docs/design-system/mockups/compiled-tokens.css create mode 100644 docs/design-system/mockups/dashboard.dc.html create mode 100644 docs/design-system/mockups/insights.dc.html create mode 100644 docs/design-system/mockups/search-and-filters.dc.html create mode 100644 docs/design-system/mockups/streamline.dc.html create mode 100644 docs/design-system/reference/bundle.css create mode 100644 docs/design-system/reference/bundle.js create mode 100644 docs/design-system/reference/index.d.ts create mode 100644 docs/design-system/tokens.json diff --git a/docs/design-system/IMPLEMENTATION.md b/docs/design-system/IMPLEMENTATION.md new file mode 100644 index 0000000..2e45fbc --- /dev/null +++ b/docs/design-system/IMPLEMENTATION.md @@ -0,0 +1,125 @@ +# Implémenter le design system Tracker « Surligneur » dans `web/` + +Tu vas refondre l'interface de Tracker (`web/`, React 19, TypeScript, Vite, Tailwind 4) pour appliquer le design system documenté dans `docs/design-system/`. Le travail est découpé en cinq lots. Chaque lot fait l'objet d'une PR séparée, et l'application doit rester fonctionnelle à la fin de chacun. + +## Sources, à lire avant tout code + +- `docs/design-system/README.md` : le livre de marque. Ses principes, la règle de couleur, la grammaire des statuts, la typographie, la voix, la densité, le mouvement et l'architecture de navigation font foi. +- `docs/design-system/tokens.json` : la source de vérité des couleurs (clair et sombre), de la typographie, des espacements, des rayons, des tailles et de l'ombre. +- `docs/design-system/components/*.md` : la spécification de chaque composant (rôle, variantes, règles d'usage, accessibilité). +- `docs/design-system/reference/` : `bundle.js` et `bundle.css` sont une implémentation de référence en React (sans JSX, avec un namespace global), et `index.d.ts` donne les props attendues. **Ne les importe pas.** Réimplémente proprement en TypeScript, mais reprends exactement les rendus, les classes visuelles, les tracés SVG (icônes, glyphes de statut) et les comportements. +- `docs/design-system/mockups/` : les maquettes de pages validées. Lis d'abord `mockups/README.md` pour comprendre leur format. +- `docs/design-system/assets/logos/` et `docs/design-system/fonts/` : le logo en SVG, et les polices Spline Sans et Spline Sans Mono (licence OFL, en secours si les paquets Fontsource posent problème). +- `CLAUDE.md`, `web/src/index.css`, `web/src/contexts/ThemeContext.tsx`, `web/src/components/Layout.tsx`, `web/src/components/ui/*`, `web/src/types/api.ts` et `proto/event/v1alpha1/event.proto` : l'existant à respecter. + +Ignore l'ancien `design.md` : il est remplacé par `docs/design-system/`. + +## Règles non négociables + +1. **Aucune couleur en dur** dans les composants et les pages (ni hex, ni rgb, ni classes de palette Tailwind comme `blue-500`). Tout passe par les tokens. +2. **La couleur signale l'écart.** Seuls `failed` et `warning` portent une couleur de statut. `done` et `open` restent neutres, et il n'y a jamais de vert de succès. `impact` (magenta) signale un impact utilisateur, quel que soit le statut. Les couleurs `type-*` ne servent que dans les graphiques. +3. **`signature` (chartreuse) marque uniquement le présent** : l'aiguille « now », ce qui est en cours, la page active et la sélection (`signature-wash`). On l'utilise toujours en aplat sous un texte `on-signature`, jamais comme couleur de texte. +4. **Un statut passe toujours par `StatusMark`, un impact par `ImpactMark`.** On ne dessine jamais un statut à la main. +5. **Spline Sans Mono pour les faits** (heures, chiffres, versions, identifiants, métadonnées en minuscules séparées par « · »), **Spline Sans pour les phrases**. Chiffres tabulaires, sans zéro de remplissage, et pas de labels en capitales espacées. +6. **Filets plutôt qu'ombres.** `shadow-overlay` est réservée au drawer, à la palette et aux menus. +7. **Clair et sombre sont égaux.** Chaque écran est vérifié dans les deux thèmes. +8. **Accessibilité** : anneau de focus `focus-ring` de 2 px sur tout élément interactif, noms accessibles pour les boutons icône, les glyphes et l'impact, rôles listbox et option sur les listes navigables, `prefers-reduced-motion` respecté, contrastes du README tenus. +9. **Textes de l'interface en anglais**, en casse de phrase. Pas d'emoji, pas de point d'exclamation. +10. **Pas de dégradés, pas d'accent violet ou bleu générique, pas de pastilles multicolores** pour le type ou l'environnement, et pas de bordure gauche colorée sur les cartes. + +## Méthode de travail + +- Au début de chaque lot, présente un plan court : fichiers touchés, décisions, risques. **Attends ma validation avant de coder.** +- Ne modifie ni le backend ni `proto/` sans me le demander. Quand une donnée manque, propose l'option côté client et l'option côté API. +- Avant de rendre un lot : `npm run lint`, `npm run build:check` et `npm test` doivent passer dans `web/`. Ajoute des tests Vitest pour la logique pure. +- Transforme `web/src/pages/ThemeTest.tsx` en vitrine vivante du design system : chaque nouveau composant y apparaît dans tous ses états, en clair et en sombre. Mets-la à jour à chaque lot. +- Garde les routes et les fonctionnalités existantes. Un écran pas encore refondu doit continuer de fonctionner. +- Fais des commits petits et explicites. + +## Lot 1 — Fondations + +1. Copie `docs/design-system/tokens.json` vers `web/src/design/tokens.json`, puis écris `web/scripts/build-tokens.mjs`, qui génère `web/src/design/tokens.css` : les tokens de couleur clairs sous `:root`, les sombres sous `.dark`, et les autres familles (espacement, rayon, taille, ombre) sous `:root`. Branche le script sur `prebuild` et `predev`. Le fichier généré porte une mention « ne pas modifier à la main ». +2. Dans `web/src/index.css`, importe `tokens.css` et déclare un bloc `@theme inline` qui expose les tokens à Tailwind : `--color-paper: var(--paper)`, et de même pour tous les tokens de couleur, plus les polices, les rayons et les espacements. Les utilitaires deviennent `bg-paper`, `text-ink`, `border-rule-strong`, `text-impact`, `bg-signature`, etc. Crée des utilitaires `@utility` pour les styles typographiques de `tokens.json` (`text-metric`, `text-title`, `text-summary`, `text-data-sm`…). +3. Polices : installe `@fontsource-variable/spline-sans` et `@fontsource-variable/spline-sans-mono`, et retire Plus Jakarta Sans, IBM Plex Mono et Space Grotesk. +4. **Pont de compatibilité** : redéfinis les variables `--hud-*` existantes (des composantes RGB utilisées par `rgb(var(--hud-…))`) à partir des nouveaux tokens, en clair et en sombre. `--hud-bg` reprend `paper`, `--hud-surface` reprend `surface`, `--hud-primary` reprend `ink`, `--hud-on-surface-var` reprend `muted`, `--hud-outline-var` reprend `rule`, `--hud-error` reprend `failed`, `--hud-warning` reprend `warning`, et `--hud-success` reprend `muted` (le succès reste neutre). Toute l'application adopte ainsi la palette sans toucher les fichiers qui utilisent `hud-*`. Marque ce bloc comme temporaire. +5. Crée `web/src/lib/status.ts`, avec ses tests : + - `toDsStatus(apiStatus)` : `failure` et `error` donnent `failed` ; `warning` donne `warning` ; `start` et `in_progress` donnent `running` ; `success`, `done` et `close` donnent `done` ; tout le reste (`open`, `planned`, `waiting_approval`, `snapshot`, `user_update`, `recommandation`, non spécifié) donne `open`. + - `toTypeIcon(type)` : `deployment`, `operation`, `drift` (icône `drifts`), `incident` et `rpa_usage` (icône `rpa`). +6. Ajoute à `CLAUDE.md` une section « Design system » qui pointe vers `docs/design-system/` et reprend les dix règles ci-dessus. + +Le lot 1 est terminé quand l'application tourne avec la nouvelle palette et les nouvelles polices dans les deux thèmes, sans régression fonctionnelle, et quand la vitrine affiche les couleurs et les styles typographiques. + +## Lot 2 — Composants de base, dans `web/src/components/ui` + +Implémente en TypeScript, avec `class-variance-authority` quand il y a des variantes, en suivant `components/*.md`, `reference/index.d.ts` et le rendu de `reference/bundle.js` et `bundle.css` : + +- **`Icon`** : le jeu d'icônes sur mesure. Reprends les tracés de `icons` dans `bundle.js`, sur une grille de 16 px, trait de 1.5, extrémités carrées. Expose `name`, `size` et `title`. +- **`Logo`** : symbole et logotype, à partir des tokens. Les SVG de `assets/logos` servent hors application (favicon, README). +- **`Button`** : variantes `primary`, `secondary` et `ghost`, tailles `md` et `sm`, bouton icône. Fusionne avec l'actuel `button.tsx` et `button-variants.ts`, ou remplace-les. +- **`Kbd`** et **`Highlight`**. +- **`StatusMark`** : étiquette de 22 px avec glyphe à signe intérieur, ou glyphe seul avec `label={false}`. Les tracés des glyphes sont dans `Glyph` de `bundle.js`. La rotation de `running` s'arrête avec `prefers-reduced-motion`. +- **`ImpactMark`** : versions compacte et avec libellé. +- **`SegmentedControl`** : sur `@radix-ui/react-toggle-group`, ou des boutons avec `aria-pressed`. +- **`MetricStrip`** : avec `tone` (`failed`, `warning`, `impact`), `delta` et `trend`. Seul `trend: 'worse'` est coloré. +- **`EventRow`** et **`EventList`** : trois colonnes (heure, titre avec métadonnées, puis `ImpactMark` et `StatusMark` à droite), densités confort et compacte, `role="listbox"`, sélection et navigation ↑ ↓ au clavier. +- **`Sidebar`** : groupes, page active en `signature`, compteurs, et mode `collapsed` en rail de 60 px avec infobulles. +- **`TimeRuler`** : props `startHour` et `endHour`. +- **`EventDrawer`** : mise en page « bandeau » (type, statut, titre, pastilles service, environnement et owner qui déclenchent `onFilter`, liens PR et ticket avec emplacement vide en pointillé, bande d'impact, détails, historique tiré du changelog). +- **`CommandPalette`** : sur `cmdk` dans un Dialog Radix, avec groupes, raccourcis et navigation clavier. + +Chaque composant apparaît dans la vitrine et a au moins un test de rendu et d'accessibilité : nom accessible, rôle, état. + +## Lot 3 — Coquille et navigation + +- **`Layout.tsx`** : la nouvelle `Sidebar` (quatre groupes : operations, services, infrastructure, resources), une barre du haut avec le bouton « Search or jump to… ⌘K » qui ouvre la `CommandPalette`, et **un seul bouton `New`** avec menu (event, lock, drift, service, RPA), branché sur `CreatePanelContext`, à la place des cinq boutons actuels. Supprime les emojis du pied de page. +- **Hook de raccourcis** : `/` focalise le filtre de la page, ⌘K ouvre la palette, G puis D, T ou S navigue, esc ferme. Les raccourcis sont ignorés pendant la saisie dans un champ. +- **`QueryBar`**, nouveau composant décrit dans `mockups/search-and-filters.dc.html` et `mockups/streamline.dc.html` : + - syntaxe `key:value`, `key:a,b`, `-key:value`, `"phrase"` et texte libre ; + - clés `env`, `status`, `type`, `owner`, `service`, `priority`, `impact` ; + - autocomplétion des clés, puis des valeurs avec leur nombre de résultats ; + - jetons supprimables, ⌫ sur un champ vide retire le dernier jeton. +- **Parseur `parseQuery` et `serializeQuery`**, testé, avec l'état des filtres dans l'URL. Les filtres et la période sont partagés entre Timeline, Streamline et Calendar. Traduis les filtres vers les paramètres de `SearchEvents` quand ils existent. `owner` n'y est pas : filtre côté client et signale-le-moi. +- **Vues enregistrées** (onglets avec compteur, « Save view ») stockées en localStorage pour commencer. + +## Lot 4 — Pages, dans cet ordre + +Pour chaque page, suis la maquette correspondante. La mise en page, les textes, les composants et les comportements de `renderVals()` font foi. Remplace les données d'exemple par celles de l'API. + +1. **Dashboard** (`mockups/dashboard.dc.html`) : + - phrase de synthèse dont chaque segment filtre l'activité ; + - `MetricStrip`, dont la métrique « users impacted » ; + - Needs attention (échecs, impacts, chevauchements à venir, warnings) et Up next (planifié, chevauchements, locks) ; + - Activity avec filtres par statut et leurs compteurs, histogramme des 24 h cliquable, `EventList` et `EventDrawer` ; + - sélecteur everyone / my team. +2. **Timeline** (pas de maquette dédiée) : `QueryBar`, `TimeRuler` en tête, `EventList` dans les deux densités et `EventDrawer`. Remplace `EventDetailsModal`, et fais en sorte que `/events/:id` ouvre le drawer. +3. **Streamline** (`mockups/streamline.dc.html`) : garde la logique de `EventsStreamline.tsx` et rhabille-la. + - vues enregistrées, `QueryBar`, regroupement par environnement ou par équipe, option « Overlaps only » ; + - navigateur de chevauchements Previous / Next (raccourcis P et N), qui réutilise le calcul de `EventsOverlaps` ; + - aperçu des 24 h, couloirs, passé grisé, aiguille « now », barres stylées selon le statut avec un liseré `impact`, hachures de chevauchement et de lock ; + - drawer de résolution de chevauchement. +4. **Architecture** (`mockups/architecture.dc.html`) : garde React Flow. + - type de nœud `ServiceNode`, et disposition automatique de gauche à droite via `dagre` ou `elkjs` (les points d'entrée à gauche) ; + - clic sur un service : simulation de panne, avec le rayon d'impact calculé côté client à partir de `dependencies_in` et `dependencies_out` ; + - panneau d'impact (phrase, métriques, services touchés directement et en cascade, équipes à prévenir via `communication_channels`, événements du jour), mode « what it needs », vue classement par rayon d'impact ; + - `Sidebar` repliée par défaut, et KPI ainsi que filtres en pastilles supprimés. +5. **Insights** (`mockups/insights.dc.html`) : garde Recharts, avec des couleurs en `var(--type-*)`, `var(--impact)` et `var(--ink)`. + - `MetricStrip` comparée à la période précédente ; + - activité quotidienne empilée par type avec la période précédente en pointillé ; + - répartition par type ; + - tableau « Reliability and impact by service », triable ; + - tendance sur 12 mois avec un sélecteur de mesure ; + - **pas de phrase de synthèse en haut de cette page**. + + Calculs : taux d'échec = déploiements en échec ÷ déploiements (via `GetEventStats` filtré) ; rétablissement = médiane de `metadata.duration` des incidents ; impact = durée cumulée des événements avec `impact = true` ; tendance via `GetEventStatsByMonth`. Signale-moi ce qui demanderait un endpoint dédié. +6. **Le reste**, avec les mêmes composants et règles : Calendar (surcharge le thème Schedule-X avec les tokens), Overlaps, Catalog, Compliance, Drifts, RPA usage, Locks, Links, Docs, ainsi que les formulaires de création. + +## Lot 5 — Nettoyage et garde-fous + +- Supprime le pont `--hud-*`, puis toute référence restante à `hud-*`. Supprime aussi les couleurs en dur (`T.primary`…), les `.icon-gradient`, Font Awesome et Lucide, une fois toutes les icônes passées au jeu sur mesure. +- Ajoute une règle de lint qui interdit les couleurs en dur et les classes de palette Tailwind dans `web/src`, sauf dans `web/src/design/`. +- Fais une passe d'accessibilité : navigation clavier complète, focus visible, contrastes, `prefers-reduced-motion`. +- Mets à jour `web/README.md` et les captures de `docs/assets/screenshots/`. + +## Pour commencer + +Lis toutes les sources listées plus haut, puis présente-moi le plan du lot 1. N'écris pas de code avant ma validation. diff --git a/docs/design-system/README.md b/docs/design-system/README.md new file mode 100644 index 0000000..b7a9f13 --- /dev/null +++ b/docs/design-system/README.md @@ -0,0 +1,115 @@ +# Tracker + +L'instrument de bord des équipes ops : ce qui s'est passé, ce qui se passe, ce qui coince. + +Tracker suit les déploiements, incidents, drifts, opérations et locks d'une infrastructure distribuée. Il est ouvert par tout le monde : le SRE d'astreinte qui creuse un échec, le dev qui vérifie son déploiement, le lead qui veut savoir en trois secondes si la journée est calme. L'identité, baptisée **Surligneur**, s'inspire du carnet d'ingénieur : papier chaud, encre, trame millimétrée, et un trait de surligneur chartreuse pour ce qui compte maintenant. + +## Principes + +1. **La couleur signale l'écart.** Seuls `failed` et `warning` portent une couleur de statut, et `impact` signale un impact utilisateur. Ce qui s'est bien passé (`done`) et ce qui attend (`open`) restent dans les neutres (`muted`, `chart-done`, `chart-open`). Le principe a été testé sur le dashboard face à une version « succès en vert » : en neutre, les deux échecs ressortent seuls, et un taux de succès de 60 % n'envoie plus de signal positif trompeur. +2. **Le chartreuse marque le présent.** `signature` signale l'aiguille « now », un événement en cours, la page active de la navigation et la sélection (`signature-wash`). Rien d'autre. S'il apparaît ailleurs, il perd son sens. +3. **Le mono pour les faits, la sans pour les phrases.** Heures, chiffres, versions, identifiants et métadonnées sont en Spline Sans Mono. Les titres et les phrases sont en Spline Sans. +4. **Deux niveaux de lecture.** Chaque écran commence par une phrase de synthèse (style `summary`), puis viennent les chiffres, et le détail s'ouvre au clic dans le drawer. On ne force jamais le lecteur pressé à décoder un graphique. +5. **Filets, pas d'ombres.** Les panneaux se posent sur `paper` et se délimitent par un filet `rule`. `shadow-overlay` est réservée à ce qui flotte : drawer, palette ⌘K, menus. +6. **Deux thèmes égaux.** Clair et sombre sont conçus ensemble. Chaque token a ses deux valeurs, et aucun écran n'est validé tant que les deux n'ont pas été regardés. + +## Couleur + +Les neutres sont chauds et ne descendent jamais au blanc ou au noir purs : `paper` (#F3F1EC / #131210), `surface`, `sunken`, `overlay`, avec l'encre `ink` et le secondaire `muted`. Les bords décoratifs utilisent `rule` et `rule-soft`. Un contrôle interactif est toujours bordé de `rule-strong`, qui atteint 3:1. + +`signature` (#D4F53C) s'utilise toujours en aplat avec `on-signature` par-dessus. Ne jamais écrire en chartreuse sur `paper` : le contraste est d'environ 1.1:1. Dans les graphiques, l'aiguille « now » utilise `needle` (encre le jour, chartreuse la nuit), et le libellé « now 15:31 » est un surligneur. + +Les types d'événements ont chacun une couleur de série, réservée aux graphiques : `type-deployment` (cobalt), `type-operation` (sarcelle), `type-drift` (violet), `type-rpa` (ardoise). Les incidents restent en `ink`. Ces teintes ont été choisies pour ne se confondre ni avec les statuts (`failed`, `warning`), ni avec `signature`, ni avec `impact`. On ne les utilise jamais pour colorer une ligne, une étiquette ou un fond : un type se lit par son icône dans l'interface, et par sa couleur seulement dans un graphique. + +Les autres données catégorielles (environnements, équipes) utilisent la rampe neutre `data-1` à `data-4`, jamais les couleurs de statut. Au-delà de quatre séries, on regroupe le reste en « other ». + +`impact` (magenta, #B5126F en clair, #FF5CB0 en sombre) est la seule couleur hors statut. Elle signale qu'un événement touche les utilisateurs (attribut `impact` de l'événement), quel que soit son statut : un déploiement réussi peut avoir un impact, un échec peut ne pas en avoir. On l'utilise en aplat (`impact` avec `on-impact`) pour la bande du drawer, ou en fond discret (`impact-wash`) pour un marqueur dans une liste. Jamais pour un statut, une catégorie ou une décoration. + +L'impact doit se voir sur chaque page, pas seulement dans le détail : marqueur `ImpactMark` dans chaque ligne d'événement et dans les listes de triage, liseré sur les barres de Streamline, métrique « users impacted » dans les bandes de métriques, segment dédié dans la phrase de synthèse, clé `impact:true` dans les filtres et vue enregistrée « Impact ». + +À ne pas faire : dégradés, violet ou bleu d'accent, badges de toutes les couleurs, vert de succès, couleur sur un élément purement décoratif. + +## Grammaire des statuts + +Un statut s'affiche comme une **étiquette** : un glyphe de 12 px à signe intérieur et un libellé mono, dans un contenant de 22 px. Chaque glyphe porte un signe (✕, !, ✓, arc ouvert), pour rester identifiable en niveaux de gris comme pour un lecteur daltonien. + +| Statut | Glyphe | Étiquette | Poids visuel | +| --- | --- | --- | --- | +| failed | carré plein marqué d'un ✕ | fond `failed-wash`, texte `failed` | fort | +| warning | triangle plein marqué d'un ! | fond `warning-wash`, texte `warning` | fort | +| running | arc ouvert et point, rotation lente | fond `signature`, texte `on-signature` | fort, c'est le présent | +| done | cercle et ✓ | filet plein `tag-line`, texte `muted` | faible | +| open | cercle vide | filet pointillé `tag-line`, texte `muted` | faible | + +Dans une liste, les étiquettes s'alignent sur le bord droit : en descendant la colonne, l'œil ne s'arrête que sur les écarts et sur ce qui est en cours. Le ✓ de done reste gris : il confirme sans célébrer, ce qui garde intact le principe « la couleur signale l'écart ». Ce choix a été testé face à trois autres lectures (glyphes seuls, symboles sans contenant, colonne de statut en tête de ligne). + +Hors des listes, quand l'étiquette serait trop lourde (historique du drawer, légendes, cellules de calendrier), on affiche le glyphe seul, coloré par son ton. Le composant `StatusMark` porte cette grammaire : on ne dessine jamais un statut à la main. + +## Typographie + +Deux familles de la même superfamille, fournies en fichiers variables (OFL) : **Spline Sans** (`--font-sans`) et **Spline Sans Mono** (`--font-mono`). + +- Les chiffres sont toujours en mono, avec `font-variant-numeric: tabular-nums`, sans zéro de remplissage : on écrit `2`, pas `02`. +- Les métadonnées sont en minuscules, en mono `data-sm`, séparées par « · » : `deployment · pré-production · P2 · platform-team`. +- On n'utilise pas de labels en capitales espacées. Un label de métrique s'écrit `events / 24h`, pas `TOTAL EVENTS`. +- Les titres sont en casse de phrase. L'interlettrage est négatif sur les grandes tailles, comme l'indique chaque style. + +## Voix et contenu + +L'interface est en anglais, en casse de phrase, avec des verbes concrets. + +- **Heures** au format 24 h : `15:04`, avec les secondes dans le drawer (`15:04:12`). +- **Dates** au format `07 apr 2026`, ou `today` et `yesterday` quand c'est plus clair. +- **Cibles de déploiement** avec une flèche : `Deploy monitoring v2.5.5 → mco`. +- **Phrase de synthèse** construite selon la formule « ce qui demande une action, puis ce qui est en cours, puis ce qui va bien » : *2 deployments failed and need attention. 1 deployment is running on production. 3 events completed, no scheduling overlaps.* +- **États vides** factuels plutôt qu'enjoués : `No overlaps in the last 7 days.` +- Pas d'emoji, pas de points d'exclamation, pas de « Oops ». + +## Espace et densité + +Grille de 4 px (`space-1` à `space-16`). Les pages ont une marge de `space-8` et les panneaux un padding de `space-5`. Les blocs sont séparés de `space-6`. + +Les listes et tableaux existent en deux densités, réglables par l'utilisateur et mémorisées : + +| Densité | Padding de ligne | Hauteur | Contenu | +| --- | --- | --- | --- | +| Confort (défaut) | `row-pad-comfy` 12 px | ≈ 62 px | titre puis métadonnées sur deux lignes | +| Compact | `row-pad-compact` 6 px | ≈ 32 px | titre et métadonnées sur une ligne, tronqués | + +## Motif : la règle graduée + +Le temps est la matière de Tracker, donc l'axe du temps est le motif de la marque. Le composant `TimeRuler` affiche des graduations sur une trame millimétrée `grid` au pas de 8 px, des repères majeurs à l'heure en `ink`, des marques d'événements colorées selon leur statut, et l'aiguille `needle` surmontée d'un surligneur « now ». On le trouve en tête de Timeline, de Streamline et dans le graphique de vélocité. On ne l'utilise pas comme décor. + +## Iconographie + +Le jeu d'icônes est dessiné pour Tracker. Il est exposé par `Tracker.Icon` et visible dans la carte Icon. + +- Grille de 16 px, trait de 1.5 px, extrémités carrées, angles vifs, sans remplissage. +- Les formes viennent du vocabulaire de l'instrument : axes, graduations, aiguilles, barres. +- On ne trouve ni robot, ni fusée, ni bouclier, ni clé à molette. Le triangle est réservé au statut warning et n'apparaît jamais dans une icône. +- Les glyphes de statut forment une famille à part : extrémités arrondies et signe intérieur, là où les icônes ont des extrémités carrées et restent vides. +- Les icônes servent à la navigation, aux types d'événements, aux faits d'un événement (`service`, `environment`, `owner`, `impact`, `pull-request`, `ticket`) et aux actions. Elles ne décorent pas un titre ou une carte. +- Couleur `muted` au repos, `ink` au survol, `on-signature` sur la page active. + +## Mouvement + +Le mouvement est bref et n'a qu'une seule direction. + +- **Durées** : 120 ms pour les survols et les fondus, 180 ms pour le drawer. +- **Courbe** : `cubic-bezier(0.2, 0, 0, 1)`, sans rebond. +- **Drawer** : il glisse depuis la droite sans voile, pour que la liste reste visible. +- **Palette ⌘K** : elle apparaît en fondu, avec un léger déplacement de 4 px. +- **Statut running** : l'arc ouvert du glyphe tourne en 1.6 s. Avec `prefers-reduced-motion`, plus rien ne bouge. + +## Architecture et patterns + +- **Navigation** : barre latérale de `sidebar-w`, en quatre groupes (operations, services, infrastructure, resources). La page active est surlignée en `signature`. +- **Vues temporelles** : Timeline, Streamline et Calendar restent trois pages, mais partagent une `FilterBar` commune. La période et les filtres suivent l'utilisateur d'une vue à l'autre. +- **Création** : un seul bouton `New` avec menu (event, lock, drift, service, RPA), doublé de la palette ⌘K qui sert aussi à chercher et à naviguer. +- **Détail** : un clic sur un événement ouvre l'`EventDrawer` à droite (`drawer-w`). La liste reste visible et navigable au clavier (↑ ↓), et le drawer suit la sélection. +- **Densité** : un réglage confort ou compact, global, dans le menu utilisateur. +- **Impact** : présent sur toutes les pages qui listent ou résument des événements (voir `ImpactMark`). Une page qui montre des événements sans leur impact est incomplète. + +## Logo + +Le symbole est un T dont la barre est un trait de surligneur : un fût `surface` sur un carré `ink`, barré de `signature`. Le logotype « tracker » est composé en Spline Sans SemiBold, en minuscules, avec un interlettrage de −0.035 em. Les fichiers se trouvent dans le groupe d'assets Logos, en version encre (fonds clairs) et inverse (fonds sombres). On ne recolore pas le logo, on ne remplace pas le chartreuse, et on ne pose jamais le symbole sur un fond `signature`. diff --git a/docs/design-system/assets/logos/README.md b/docs/design-system/assets/logos/README.md new file mode 100644 index 0000000..c956d41 --- /dev/null +++ b/docs/design-system/assets/logos/README.md @@ -0,0 +1,10 @@ +# Logos + +Quatre fichiers vectoriels, avec un logotype vectorisé depuis Spline Sans SemiBold, donc sans dépendance à la police. + +- `tracker-lockup.svg` : symbole et logotype en encre #141311, pour les fonds `paper` et `surface` clairs. +- `tracker-lockup-inverse.svg` : symbole inversé (carré #EDEAE3, fût #141311) et logotype #EDEAE3, pour les fonds sombres. +- `tracker-mark.svg` : symbole seul, carré #141311 et fût #FAF8F3. Sert de favicon et d'avatar. +- `tracker-mark-inverse.svg` : symbole seul pour les fonds sombres. + +La barre du T est toujours `signature` (#D4F53C). La zone de protection autour du logo vaut la largeur du fût du T, soit 6/44 de la hauteur du symbole. Le symbole ne descend pas sous 16 px de haut, et le logotype complet pas sous 20 px. diff --git a/docs/design-system/assets/logos/tracker-lockup-inverse.svg b/docs/design-system/assets/logos/tracker-lockup-inverse.svg new file mode 100644 index 0000000..2f662ac --- /dev/null +++ b/docs/design-system/assets/logos/tracker-lockup-inverse.svg @@ -0,0 +1 @@ +AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTpjMTlhMjFiOC1lNjBjLTQwM2ItYjFiOS02Nzk1YzViY2FkNjgAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaE+9GkL0GOIygNt/rwFFlCAAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDpjMzQyZmRhYi03YTBhLTQ4YTUtYmE0Yy02MjA2NWY5YjYwMzdscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNokxog4vlcxNvfbnJhtJJFzgAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggt2QjveJwJs7Lj4bf8NiXcXgz+2w57Ay31zC9CkpEuPOkZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaPsTeIYKPe9TttEXRzERH58AAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCD/p1+Uv5L5TD0TjRZ9CKYXtyb0438As3XrPvrWS20anWRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBi3Zmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOmMxOWEyMWI4LWU2MGMtNDAzYi1iMWI5LTY3OTVjNWJjYWQ2OC9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOmQ5N2U5NGViLWNjYWMtNDllNy1iN2E3LTEyZmNkNmQzNTM0M3JjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCC3ZCO94nAmzsuPht/w2JdxeDP7bDnsDLfXML0KSkS486JjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFggIH5UGJXbBJ4kMCOr31F0N6jPkzK/cdszdXjHOzEfa7aiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggkzXdWWTrT4Nz0XjIGS6nSKRrmT4o8RV/Qr5iUgZeFUJ0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZYQOLtPBpnNJ9JseNK3xg5KcghP5OyN70hahlUE0YsiPgg4k90GQHQNjbbRMql8FV0yh0xz/TMrfKdZNdx464k7Ys= diff --git a/docs/design-system/assets/logos/tracker-lockup.svg b/docs/design-system/assets/logos/tracker-lockup.svg new file mode 100644 index 0000000..f4e2f85 --- /dev/null +++ b/docs/design-system/assets/logos/tracker-lockup.svg @@ -0,0 +1 @@ +AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTpiM2RjY2M1ZS1hODBlLTQ1ZTYtOTQ1MS0xN2RiNDUwYTcxZmMAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaEjxnfOrN7xQFRxAsucadGAAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDo2MTBjMGY4ZC02YmQwLTRlZTEtYTFhZi1kZDZjODBmZGVkMmFscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNoDccCC1i7WNRmu5GYRLv0KAAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggZWrDb18JgLULW7gIPCJ4n/KeDplvNyQMt7S+wLsYbe6kZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaKNvLnWVLN2N/U2eP0eeySkAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCDgK+XcVcxTOxc3I+m0PGT9spBAlxLPhtrcysNvAWia4mRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBi3Zmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOmIzZGNjYzVlLWE4MGUtNDVlNi05NDUxLTE3ZGI0NTBhNzFmYy9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjBmZGRiNTk1LTY4MTAtNDg1Mi1hNmMwLWRhOWQ5NmM3MDRjMnJjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCBlasNvXwmAtQtbuAg8Inif8p4OmW83JAy3tL7Auxht7qJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFggP1meGqV4vY3zLMCs7KKNXI26MJkZrhiXfkM5P26cQfGiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggOumhzzq9aQeFXREzv8sF3jUsNcqg+v7FYtrBkJKc6rh0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZYQMnJPkFWonQpvcWA/wspBIHtVXn2NNyymjNO0JswgYLSsbyJUgi7fBwdhcxnEFd8RNiEc/Cpqsva3G4j8N1ZgEA= diff --git a/docs/design-system/assets/logos/tracker-mark-inverse.svg b/docs/design-system/assets/logos/tracker-mark-inverse.svg new file mode 100644 index 0000000..7d05ecd --- /dev/null +++ b/docs/design-system/assets/logos/tracker-mark-inverse.svg @@ -0,0 +1 @@ +AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTozMjM4MzQ4Yy00ODM3LTQ0MzMtYjE1MS02N2E4MTI5MmE3MmEAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaB1WyOZyseph4iOYA+JWsXUAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDpjZGVjMDU1NC03YWEwLTQ0ZjctOWZmOC00MGEyMTNhNmM0MmJscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNo/KLSg3Rf/WKyYSKYnE+ZUwAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggjGgXT/nWTzYzO/rroXE5n7jlPR3wHS7wA4pAbi4/87SkZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaIZTv85VjcY1WEe8vWt+z2AAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCCN8MiJs5xSrWfHupZyogVpX0j0fCUwkWfHRschbDFfVmRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBiyZmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOjMyMzgzNDhjLTQ4MzctNDQzMy1iMTUxLTY3YTgxMjkyYTcyYS9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjMxOTMyZjkzLWYzNTItNGZmZC04ZTQ3LWI1MWZhNWZjMmYzMHJjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCCMaBdP+dZPNjM7+uuhcTmfuOU9HfAdLvADikBuLj/ztKJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFgg+l1If06lU/TJiUElSEIl7GAAEmKWZcBsDEwXU2TdLj+iY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFgg3mVLKcgGftRcSk5FuJlzfIuslSDNX3K2UWOksHm5rSZ0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZYQKX+arcn2aC9fdhBs+prhPCNwGfBJ7rf/X9Zh6/9pY1AqtOinKJCB05ocxt3m5IDinTYFGmU7L7LHQPejTLdQk4= diff --git a/docs/design-system/assets/logos/tracker-mark.svg b/docs/design-system/assets/logos/tracker-mark.svg new file mode 100644 index 0000000..ba268e1 --- /dev/null +++ b/docs/design-system/assets/logos/tracker-mark.svg @@ -0,0 +1 @@ +AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTozMTU1YmEzMy1iYmJlLTQ3NDUtYTI5Ni02M2QzZGJmYTY3OTIAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaMRhYxdwAPJq+X84t2Ifu+sAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDo1NjI0NjhlYi02NmExLTRiNDgtOGE4OS1mNTQ5YTE4NWEzODlscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNov0fAz2vo7D5yIRNRXdpHIQAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggoaxnRNQw9WojCEzy/zc90EYrT7geQfkEF+YnstkgCq6kZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaHPveTbXUQWit+nnlN4D3AkAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCBupovRpLT/T8jDZBEozaeBcm8RPJOhKpzZJwCHfK3ZZ2RuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBiyZmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOjMxNTViYTMzLWJiYmUtNDc0NS1hMjk2LTYzZDNkYmZhNjc5Mi9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOmYzYTE4Y2YxLTkyNDctNGJmMS1hN2E0LTVkNjdhZGZlMzNlM3JjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCChrGdE1DD1aiMITPL/Nz3QRitPuB5B+QQX5iey2SAKrqJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFggVFn2ZODkEed+erMB4wavnXsoPgrQrcB5fAJoXnhsRpOiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggde7u9iMOqNpf32QRx5xOfi/HNK/bYBgeO662c2NLcUh0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZYQKRllsHdtZJpbkXl/Tf+K79IBnbERPbOs7fYd8kIz0tKonAafRmJXhzW4WDTbpqy8+zSXM787laffE+RTSgeY+k= diff --git a/docs/design-system/components/Button.md b/docs/design-system/components/Button.md new file mode 100644 index 0000000..01e5510 --- /dev/null +++ b/docs/design-system/components/Button.md @@ -0,0 +1,10 @@ +# Button + +Trois variantes, deux tailles. Le bouton primary en encre est rare : un seul par zone. + +- **primary** (`ink` / `on-ink`) : l'action principale de la page ou du drawer, par exemple `New` dans la barre du haut ou `Edit event` dans le drawer. +- **secondary** (`surface`, bord `rule-strong`) : les actions courantes comme `Lock service` ou `Export`. +- **ghost** : les actions tertiaires et les boutons icône (fermer, copier). +- **sm** (`control-sm`, 30 px) : à utiliser dans les barres de filtres et les en-têtes de panneau. + +Le bouton `New` porte un chevron (`iconRight="chevron-down"`) et ouvre un menu : event, lock, drift, service, RPA. On n'aligne jamais plusieurs boutons « New … » côte à côte. Le chartreuse n'est jamais une couleur de bouton : il marque le présent, pas une action. Le focus clavier dessine un anneau `focus-ring` de 2 px. diff --git a/docs/design-system/components/CommandPalette.md b/docs/design-system/components/CommandPalette.md new file mode 100644 index 0000000..44fbc1e --- /dev/null +++ b/docs/design-system/components/CommandPalette.md @@ -0,0 +1,10 @@ +# CommandPalette + +La palette ⌘K : chercher, créer et naviguer depuis un seul champ. Elle double le bouton `New`. + +- Elle s'ouvre avec ⌘K, ou avec `/` hors d'un champ, au-dessus d'un voile `scrim`. Elle utilise `overlay`, `shadow-overlay` et `radius-lg`. +- Les groupes s'affichent dans un ordre fixe : Jump to, Create, puis les résultats (events, services, teams). +- Chaque création a une lettre de raccourci (`E` event, `L` lock, `D` drift, `S` service, `R` RPA). +- L'item actif prend `signature-wash`. On le déplace avec ↑ ↓, ↵ l'ouvre et esc ferme la palette. +- Un résultat d'événement porte son statut et son heure en indice (`failed · 15:04`). +- Pour une recherche sans résultat, on affiche `No results for “…”` sans illustration. diff --git a/docs/design-system/components/EventDrawer.md b/docs/design-system/components/EventDrawer.md new file mode 100644 index 0000000..4353a45 --- /dev/null +++ b/docs/design-system/components/EventDrawer.md @@ -0,0 +1,24 @@ +# EventDrawer + +Le détail d'un événement, dans un panneau latéral de `drawer-w` ouvert à droite de la liste. Sa mise en page « bandeau » met en avant les faits sur lesquels on agit, tous dans l'en-tête. + +**En-tête, sur fond `sunken` :** + +- Le type en étiquette (icône du type et nom : deployment, operation, drift, incident, rpa usage), le `StatusMark`, l'heure, puis le bouton de fermeture. +- Le titre, en style `title`. +- Les pastilles service, environnement et owner, avec leurs icônes. Chaque pastille est un bouton qui filtre les vues temporelles sur sa valeur (`onFilter('service' | 'env' | 'owner', valeur)`). Les stakeholders figurent dans l'infobulle de l'owner. La priorité suit, en pastille discrète non cliquable. +- Les liens : `PR #1287` et la clé du ticket, en boutons qui ouvrent la source dans un nouvel onglet. Un lien absent (`null`) devient un emplacement en pointillé avec un bouton +. On le montre volontairement, pour inciter à compléter l'événement. + +**Bande d'impact.** Si `impact` vaut `true`, une bande pleine largeur en `impact` / `on-impact` affiche « Users impacted » et une note (canal notifié, status page). Si `impact` vaut `false`, une ligne discrète indique « No user impact declared ». L'impact est indépendant du statut : un déploiement réussi peut avoir un impact. C'est la seule couleur hors statut du système. + +**Corps :** la phrase de résumé si elle existe, puis `details` pour les champs secondaires (source, horaires, notifications, identifiant), et `history`, le changelog avec ses glyphes. + +**Pied :** au plus trois actions, avec une seule primary. + +**Comportement :** + +- Il n'y a pas de voile : la liste reste visible, et ↑ ↓ dans la liste font défiler le drawer d'un événement à l'autre. esc le ferme. +- Il glisse depuis la droite en 180 ms, `cubic-bezier(0.2, 0, 0, 1)`, et apparaît sans animation avec `prefers-reduced-motion`. +- Le drawer peut s'ouvrir via une URL (`?event=`), pour qu'un lien partagé ouvre directement le détail. + +Rétrocompatibilité : sans service, environnement ni owner, l'en-tête revient à la ligne de métadonnées `meta`, et la zone de liens n'apparaît que si `pullRequest` ou `ticket` est fourni. diff --git a/docs/design-system/components/EventRow.md b/docs/design-system/components/EventRow.md new file mode 100644 index 0000000..96f0b25 --- /dev/null +++ b/docs/design-system/components/EventRow.md @@ -0,0 +1,14 @@ +# EventRow + +La ligne d'événement, la brique la plus répétée de Tracker. On la trouve dans Timeline, dans le flux d'activité du dashboard, dans Overlaps et dans les résultats de recherche. + +Sa structure est fixe sur trois colonnes : l'heure en mono `muted`, le titre en `body-strong` avec la ligne de métadonnées en dessous, puis l'étiquette `StatusMark` alignée à droite sur la première ligne du titre. Il n'y a pas de glyphe en tête de ligne : le statut se lit en descendant le bord droit, où seuls les écarts (failed, warning) et ce qui est en cours (running) ressortent. + +- **Densité confort** (par défaut, `row-pad-comfy`) : le titre et les métadonnées tiennent sur deux lignes. +- **Densité compacte** (`density="compact"`, `row-pad-compact`) : tout tient sur une ligne, les métadonnées sont tronquées. +- La ligne sélectionnée prend `signature-wash`. Une sélection ouvre l'`EventDrawer`, et ↑ ↓ déplacent la sélection. +- `impact` ajoute un `ImpactMark` compact, en magenta, juste avant l'étiquette de statut. L'impact se lit ainsi sur le même bord droit que le statut, sans changer la structure de la ligne. +- Les métadonnées se passent sous forme de liste ordonnée : type, environnement, priorité, équipe. Elles sont jointes par « · ». +- Pas de pastilles colorées pour le type ou l'environnement. La couleur appartient au statut. + +Utilisez `EventList` pour une liste : elle porte le rôle `listbox` et gère la sélection. diff --git a/docs/design-system/components/FilterBar.md b/docs/design-system/components/FilterBar.md new file mode 100644 index 0000000..9bcd20a --- /dev/null +++ b/docs/design-system/components/FilterBar.md @@ -0,0 +1,8 @@ +# FilterBar + +La barre de filtres partagée par Timeline, Streamline et Calendar. La période et les filtres suivent l'utilisateur d'une vue à l'autre. + +- Chaque filtre est un jeton `clé valeur` en mono (`env production`), bordé de `rule-strong`, avec un bouton de retrait accessible. +- `+ Filter` ouvre un sélecteur de clés : type, environment, priority, status, owner, service. +- `scope` rappelle discrètement où s'appliquent les filtres (`synced · timeline, streamline, calendar`). C'est ce qui rend la synchronisation compréhensible. +- L'état des filtres vit dans l'URL, pour qu'une vue filtrée puisse se partager par lien. diff --git a/docs/design-system/components/Highlight.md b/docs/design-system/components/Highlight.md new file mode 100644 index 0000000..8c435ee --- /dev/null +++ b/docs/design-system/components/Highlight.md @@ -0,0 +1,9 @@ +# Highlight + +Le surligneur : un aplat `signature` sous un texte `on-signature`, signature visuelle de Tracker. + +Il ne sert qu'à marquer le présent : ce qui est en cours dans une phrase de synthèse, le libellé « now » de la règle, le statut running. Si deux surligneurs se disputent l'attention sur un écran, l'un des deux est de trop. + +- Dans du texte courant, utilisez la version par défaut. +- Pour une donnée (`running`, `now 15:31`), utilisez `data`, en mono 11 px. +- Ne jamais l'utiliser pour mettre en valeur un mot important, une nouveauté ou une promotion. diff --git a/docs/design-system/components/Icon.md b/docs/design-system/components/Icon.md new file mode 100644 index 0000000..78c4d7d --- /dev/null +++ b/docs/design-system/components/Icon.md @@ -0,0 +1,10 @@ +# Icon + +Le jeu d'icônes dessiné pour Tracker : grille de 16 px, trait de 1.5 px, extrémités carrées, angles vifs. + +Les formes viennent de l'instrument de mesure : axes, graduations, aiguilles, barres. Chaque icône hérite de `currentColor`. + +- Au repos, une icône est en `muted`. Elle passe en `ink` au survol, et en `on-signature` dans l'item de navigation actif. +- Une icône décorative n'a pas de `title` et reste masquée aux lecteurs d'écran. Une icône seule dans un bouton a besoin d'un `aria-label` sur le bouton. +- Le triangle est réservé au statut warning, donc aucune icône n'en contient. +- Pour ajouter une icône, on la dessine sur la même grille, avec les mêmes extrémités, et on l'ajoute à `Tracker.icons`. diff --git a/docs/design-system/components/ImpactMark.md b/docs/design-system/components/ImpactMark.md new file mode 100644 index 0000000..d25dc73 --- /dev/null +++ b/docs/design-system/components/ImpactMark.md @@ -0,0 +1,21 @@ +# ImpactMark + +Le marqueur d'impact utilisateur : l'icône impact (des ondes) en magenta `impact` sur un fond `impact-wash`, à la même hauteur de 22 px que l'étiquette de statut. + +Il signale qu'un événement touche les utilisateurs (attribut `impact` du modèle), quel que soit son statut. Il ne remplace jamais le statut : il s'affiche à côté de lui. + +Où il apparaît, sur chaque page : + +- **Lignes d'événement** (`EventRow` avec `impact`) : version compacte, juste avant l'étiquette de statut. +- **Listes de triage et d'agenda** (Needs attention, Up next) : version compacte à côté du titre. +- **Barres de Streamline** : liseré magenta en bas de la barre, et version compacte dans le libellé. +- **Métriques** : la valeur « users impacted » en `tone="impact"`. +- **Drawer** : la bande pleine largeur « Users impacted » (portée par `EventDrawer`). +- **Filtres** : la clé `impact:true` dans la barre de filtre, et une vue enregistrée « Impact ». + +Variantes : + +- **`compact`** : l'icône seule dans un carré de 22 px, avec « Users impacted » comme nom accessible et comme infobulle. C'est la variante par défaut dans les listes. +- **Avec libellé** (`text`, « impact » par défaut) : pour les légendes et les en-têtes, quand la place le permet. + +On ne l'utilise pas pour une alerte générique, ni pour une notion de gravité ou de priorité. diff --git a/docs/design-system/components/Kbd.md b/docs/design-system/components/Kbd.md new file mode 100644 index 0000000..c6f1384 --- /dev/null +++ b/docs/design-system/components/Kbd.md @@ -0,0 +1,5 @@ +# Kbd + +Une touche ou un raccourci clavier, en mono 11 px dans un filet `rule`. + +À utiliser dans le champ de recherche (`⌘K`), dans les items de la palette et dans les infobulles. On écrit les symboles Mac (`⌘`, `⌥`, `↵`) et on laisse l'application les traduire sur Windows et Linux (`Ctrl`). diff --git a/docs/design-system/components/Logo.md b/docs/design-system/components/Logo.md new file mode 100644 index 0000000..b4dd976 --- /dev/null +++ b/docs/design-system/components/Logo.md @@ -0,0 +1,10 @@ +# Logo + +Le symbole « T surligné » et le logotype « tracker », rendus avec les tokens pour suivre le thème. + +Utilisez `Logo` dans l'interface (barre latérale, écran de connexion). Pour un usage hors produit (README, slides, favicon), prenez les fichiers SVG du groupe d'assets Logos. + +- `size` règle la hauteur du symbole. Le logotype suit à 78 % de cette hauteur. +- `word={false}` affiche le symbole seul, par exemple dans une barre latérale repliée. +- Le carré utilise `ink`, le fût `surface` et la barre `signature`. En thème sombre, le carré devient clair automatiquement. +- Ne pas recolorer, ne pas poser sur `signature`, ne pas descendre sous 16 px. diff --git a/docs/design-system/components/MetricStrip.md b/docs/design-system/components/MetricStrip.md new file mode 100644 index 0000000..2a25fe6 --- /dev/null +++ b/docs/design-system/components/MetricStrip.md @@ -0,0 +1,10 @@ +# MetricStrip + +La bande de métriques, posée dans un creux `sunken` sous la phrase de synthèse. Elle compte trois à cinq valeurs. + +- Le libellé est en mono minuscule (`events / 24h`), la valeur en style `metric`, et une précision en `caption` peut la compléter (`3 of 5 finished`). +- `tone="failed"` ou `tone="warning"` ne s'applique que si la valeur elle-même est un problème, par exemple un nombre d'échecs supérieur à zéro. +- `tone="impact"` colore en magenta la métrique « users impacted », et seulement quand elle est supérieure à zéro. Elle a sa place sur chaque page qui résume une période. +- Un taux de succès reste neutre, même à 100 %. +- Pas de zéros de remplissage, pas d'icône décorative, pas de petite flèche de tendance colorée. +- **Comparaison avec la période précédente** : `delta` affiche l'écart en mono sous la valeur (« −3.2 pts vs prev. », « +7 min vs prev. »), et `trend` dit si c'est mieux ou moins bien. Seul `worse` prend une couleur (ambre, avec le glyphe warning). `better` reste neutre, comme tout ce qui va bien. Le signe du delta suit la valeur, pas le jugement : un taux d'échec qui baisse s'écrit « −3.2 pts » avec `trend: 'better'`. diff --git a/docs/design-system/components/SegmentedControl.md b/docs/design-system/components/SegmentedControl.md new file mode 100644 index 0000000..9da2bfd --- /dev/null +++ b/docs/design-system/components/SegmentedControl.md @@ -0,0 +1,5 @@ +# SegmentedControl + +Un choix exclusif parmi deux à cinq options courtes : période, densité, vue. + +Les options sont en mono, et l'option active se pose en `surface` dans une piste `sunken`. Pour plus de cinq options, ou des libellés longs, utilisez un menu. diff --git a/docs/design-system/components/Sidebar.md b/docs/design-system/components/Sidebar.md new file mode 100644 index 0000000..3a92139 --- /dev/null +++ b/docs/design-system/components/Sidebar.md @@ -0,0 +1,9 @@ +# Sidebar + +La navigation principale, en quatre groupes, avec des libellés de groupe en mono minuscule. + +- La page active est surlignée en `signature`, avec le texte et l'icône en `on-signature`. C'est le « vous êtes ici » de la marque. +- `count` n'affiche qu'un nombre qui appelle une action, par exemple les locks actifs ou les drifts ouverts. On ne met pas de compteur sur une page de consultation. +- La largeur vaut `sidebar-w`. Sous 960 px, la barre se replie en symbole seul, avec les icônes et des infobulles. +- **`collapsed`** donne un rail de 60 px : le symbole du logo, les icônes seules (le nom devient infobulle et nom accessible), les groupes séparés par un filet, les compteurs en petites pastilles. On l'utilise par défaut sur les pages plein écran (Architecture, Streamline en grand) et sous 960 px de large. +- `onToggle` affiche en bas un bouton pour replier ou déplier la barre. Le choix de l'utilisateur est mémorisé par page. diff --git a/docs/design-system/components/StatusMark.md b/docs/design-system/components/StatusMark.md new file mode 100644 index 0000000..222e138 --- /dev/null +++ b/docs/design-system/components/StatusMark.md @@ -0,0 +1,23 @@ +# StatusMark + +L'étiquette de statut : un glyphe à signe intérieur et un libellé mono, dans un contenant de 22 px. C'est le seul endroit où les couleurs `failed` et `warning` apparaissent. + +| status | glyphe (12 px dans l'étiquette) | étiquette | +| --- | --- | --- | +| failed | carré plein marqué d'un ✕ | fond `failed-wash`, texte `failed`, graisse 500 | +| warning | triangle plein marqué d'un ! | fond `warning-wash`, texte `warning`, graisse 500 | +| running | arc ouvert et point central, en rotation lente | fond `signature`, texte `on-signature`, graisse 500 | +| done | cercle et ✓ | filet plein `tag-line`, texte `muted`, graisse 400 | +| open | cercle vide | filet pointillé `tag-line`, texte `muted`, graisse 400 | + +**Pourquoi des étiquettes.** Dans une liste, les écarts doivent se repérer de loin, sans lire. Les deux étiquettes lavées (failed, warning) et le surligneur (running) ressortent seules. Les neutres (done, open) restent de simples filets gris, que l'œil saute. Le ✓ de done reste gris : il confirme, il ne célèbre pas. + +**Pourquoi un signe dans chaque glyphe.** À petite taille, un carré et un cercle se confondent. Le ✕, le !, le ✓ et l'arc ouvert rendent chaque statut identifiable en niveaux de gris ou pour un lecteur daltonien. Les glyphes ont des extrémités arrondies, ce qui les distingue du jeu d'icônes, dessiné en extrémités carrées. + +Règles d'usage : + +- Dans une ligne d'événement, l'étiquette se place à droite, alignée sur la première ligne du titre. Il n'y a pas de glyphe en tête de ligne. +- `label={false}` affiche le glyphe seul, à 14 px par défaut, coloré par son ton (`tk-tone--*`), avec le libellé comme nom accessible. On l'utilise là où l'étiquette serait trop lourde : historique du drawer, légendes de graphiques, cellules de calendrier. +- Une seule étiquette par ligne. Pas d'étiquette pour le type, l'environnement ou la priorité, qui restent dans la ligne de métadonnées. +- Le libellé ne se traduit pas et reste en minuscules : `failed`, `warning`, `running`, `done`, `open`. +- La rotation de running s'arrête avec `prefers-reduced-motion`. diff --git a/docs/design-system/components/TimeRuler.md b/docs/design-system/components/TimeRuler.md new file mode 100644 index 0000000..855d78f --- /dev/null +++ b/docs/design-system/components/TimeRuler.md @@ -0,0 +1,9 @@ +# TimeRuler + +La règle graduée, motif de la marque et axe du temps de Tracker. + +Elle se compose d'une trame millimétrée `grid` au pas de 8 px, de repères horaires en `ink` avec leur libellé mono, de marques d'événements colorées selon leur statut, et de l'aiguille `needle` surmontée d'un surligneur « now ». + +- On la trouve en tête de Timeline et de Streamline, et sous le graphique de vélocité. Les graduations doivent toujours correspondre aux données affichées dessous. +- `startHour` et `endHour` définissent la fenêtre en heures (`from` et `to` restent acceptés comme alias, mais `from` est réservé sur `x-import` : utilisez `start-hour` et `end-hour` dans un canevas). `now` place l'aiguille au format `HH:MM`, et `marks` place les événements. +- Ce n'est pas un élément décoratif : on ne l'utilise pas dans un en-tête sans données temporelles. diff --git a/docs/design-system/fonts/OFL-SplineSans.txt b/docs/design-system/fonts/OFL-SplineSans.txt new file mode 100644 index 0000000..ee2bacd --- /dev/null +++ b/docs/design-system/fonts/OFL-SplineSans.txt @@ -0,0 +1,93 @@ +Copyright 2021 The Spline Sans Project Authors (https://github.com/SorkinType/SplineSans) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/design-system/fonts/OFL-SplineSansMono.txt b/docs/design-system/fonts/OFL-SplineSansMono.txt new file mode 100644 index 0000000..ebcad12 --- /dev/null +++ b/docs/design-system/fonts/OFL-SplineSansMono.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Spline Sans Mono Project Authors (https://github.com/SorkinType/SplineSansMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/design-system/fonts/SplineSans.ttf b/docs/design-system/fonts/SplineSans.ttf new file mode 100644 index 0000000000000000000000000000000000000000..00a76442c3633700560f0638929dcce04b035e3d GIT binary patch literal 146896 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ʕ{^Zj2H*F>!2@`e=I*c0hdZ4bFY$BP7r?IXD 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 literal 0 HcmV?d00001 diff --git a/docs/design-system/mockups/README.md b/docs/design-system/mockups/README.md new file mode 100644 index 0000000..e2324f6 --- /dev/null +++ b/docs/design-system/mockups/README.md @@ -0,0 +1,21 @@ +# Maquettes de pages + +Ces fichiers sont les maquettes interactives validées, exportées du canevas de conception. Ce sont des **spécifications**, pas du code à réutiliser. + +Format d'un fichier `.dc.html` : + +- Le balisage HTML décrit la mise en page exacte : structure, espacements, tailles, ordre des éléments, textes de l'interface. +- `{{ … }}` est une valeur calculée. `` répète un bloc, `` l'affiche sous condition. +- `` monte un composant du design system avec ses props (en kebab-case dans l'attribut, en camelCase dans le composant). C'est la liste des composants à utiliser à cet endroit. +- Le ` + + + + + + + + + + +

+ +
+ +
+ +
+
+ + New +
+ +
+
+

Architecture

+ {{countLine}} +
+
+ + +
+
+ +
+ + {{fi.name}} + {{fi.hint}} +
+
+
No service matches “{{findText}}”
+
+
+
+
+ + + +
+ +
+ +
+
+ + +
+
+ + + + + +
+ + + + + + + + +
+
+
+
+
+ + +
+
+
+ #serviceownersladirectbreaks if down +
+ +
+ {{r.rank}} + {{r.name}}{{r.platform}} + {{r.owner}} + {{r.sla}} + {{r.direct}} + + + {{r.total}} · {{r.crit}} crit + + Simulate +
+
+
+
+
+
+ + +
+ + Select a service to simulate its failure + arrows point to what a service calls + + + down + breaks directly + breaks in cascade + unaffected + + + selected + needed directly + needed in cascade + +
+
+ − + {{zoomLabel}} + + + Fit +
+
+ F find + M switch mode + esc clear +
+
+
+ + +
+ +
+
+
+ + + + diff --git a/docs/design-system/mockups/compiled-tokens.css b/docs/design-system/mockups/compiled-tokens.css new file mode 100644 index 0000000..c4f3b39 --- /dev/null +++ b/docs/design-system/mockups/compiled-tokens.css @@ -0,0 +1,37 @@ +/* Tracker — CSS variables compiled from the Tracker design system tokens.json (light first, dark under data-theme). */ +:root, [data-theme="light"] { + --paper: #F3F1EC; --surface: #FAF8F3; --sunken: #F1EEE7; --overlay: #FCFBF8; --scrim: rgba(20, 19, 17, 0.28); + --rule: #DCD8CF; --rule-soft: #E7E3DA; --rule-strong: #8C867A; --grid: #E9E5DC; + --ink: #141311; --ink-hover: #2E2C28; --on-ink: #FAF8F3; --muted: #615C52; + --signature: #D4F53C; --on-signature: #141311; --signature-wash: #EEF5CC; --needle: #141311; --focus-ring: #141311; + --failed: #C2261C; --failed-wash: #FBE4DF; --warning: #8F5A00; --warning-wash: #F7EBD3; --on-status: #FAF8F3; --tag-line: #B5AFA3; + --impact: #B5126F; --on-impact: #FAF8F3; --impact-wash: #FBE3F0; + --chart-done: #8C867A; --chart-open: #D3CEC4; + --data-1: #141311; --data-2: #5C574D; --data-3: #9C968A; --data-4: #CFCAC0; + --type-deployment: #3355D6; --type-operation: #0E8A80; --type-drift: #7A4CC9; --type-rpa: #5F7186; + --shadow-overlay: 0 1px 0 rgba(20, 19, 17, 0.04), 0 16px 40px rgba(20, 19, 17, 0.14); + /* canvas-only extension, candidate for the system: overlap hatch */ + --overlap-hatch: rgba(143, 90, 0, 0.16); + --lock-hatch: rgba(20, 19, 17, 0.07); +} +[data-theme="dark"] { + --paper: #131210; --surface: #1B1A17; --sunken: #22211D; --overlay: #211F1C; --scrim: rgba(0, 0, 0, 0.55); + --rule: #2E2C27; --rule-soft: #262420; --rule-strong: #6E695F; --grid: #25231F; + --ink: #EDEAE3; --ink-hover: #D2CEC5; --on-ink: #141311; --muted: #A39E93; + --signature: #D4F53C; --on-signature: #141311; --signature-wash: #2A2F16; --needle: #D4F53C; --focus-ring: #D4F53C; + --failed: #FF7A6B; --failed-wash: #3D1D18; --warning: #F0B44A; --warning-wash: #382A12; --on-status: #141311; --tag-line: #55514A; + --impact: #FF5CB0; --on-impact: #141311; --impact-wash: #3A1529; + --chart-done: #6E695F; --chart-open: #3A3732; + --data-1: #EDEAE3; --data-2: #A39E93; --data-3: #6E695F; --data-4: #45423B; + --type-deployment: #7F9BFF; --type-operation: #3FC7BA; --type-drift: #B39BFF; --type-rpa: #9DB0C4; + --shadow-overlay: 0 16px 40px rgba(0, 0, 0, 0.55); + --overlap-hatch: rgba(240, 180, 74, 0.16); + --lock-hatch: rgba(237, 234, 227, 0.07); +} +:root { + --font-sans: "Spline Sans", "Helvetica Neue", Arial, sans-serif; + --font-mono: "Spline Sans Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace; + --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-16: 64px; + --radius-xs: 3px; --radius-sm: 5px; --radius-md: 6px; --radius-lg: 8px; + --control-md: 38px; --control-sm: 30px; --row-pad-comfy: 12px; --row-pad-compact: 6px; --sidebar-w: 232px; --drawer-w: 480px; +} diff --git a/docs/design-system/mockups/dashboard.dc.html b/docs/design-system/mockups/dashboard.dc.html new file mode 100644 index 0000000..963dc8b --- /dev/null +++ b/docs/design-system/mockups/dashboard.dc.html @@ -0,0 +1,374 @@ + + + + +Tracker — Dashboard + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ + New +
+ +
+
+ +
+
+

Dashboard

+ + + live · updated 15:31 · tue 07 apr 2026 + +
+
+ + +
+
+ + +

2 deployments failed and need attention. 1 deployment is running on production. 2 events are impacting users. 2 scheduling overlaps ahead, the first at 16:30. 3 events completed.

+
+ +

Nothing failed for payments-team today. 1 deployment is running on production and is impacting users. 1 overlap ahead at 16:30 on checkout.

+
+ + + +
+
+
+
+

Needs attention

+ {{attentionCount}} +
+ Open in Timeline → +
+ +
+ + + +
+ + + {{n.title}} + + {{n.meta}} +
+ {{n.action}} +
+
+ +

Nothing needs attention for this scope.

+
+
+ +
+
+
+

Up next

+ today +
+ Open in Streamline → +
+ +
+ {{u.range}} +
+ + + {{u.title}} + + {{u.meta}} +
+ + + + +
+
+
+
+ +
+
+
+

Activity

+ {{shownCount}} of {{scopeCount}} events · last 24h +
+ Open in Timeline with these filters → +
+ +
+
+ + + +
+ + hour{{hourLabel}} + + + + +
+ +
+
+ + + +
+
+ + {{h.label}} + +
+
+ +
+ +
+ +
+ No events match these filters. + Clear filters +
+
+
+
+ + + + +
+
+ + +
+ +
+ +
+
+
+
+
+ + + diff --git a/docs/design-system/mockups/insights.dc.html b/docs/design-system/mockups/insights.dc.html new file mode 100644 index 0000000..f79e192 --- /dev/null +++ b/docs/design-system/mockups/insights.dc.html @@ -0,0 +1,321 @@ + + + + +Tracker — Insights + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + New +
+ +
+ +
+
+

Insights

+ 09 mar – 07 apr 2026 · compared with 07 feb – 08 mar +
+
+
+ + All environments, teams and services + Filter +
+ +
+
+ + + +
+
+
+
+

Activity

+ changes per day · {{activityTotal}} +
+
+ + + +
+
+
+
+ +
+ + + +
+
+
+ +
+ + {{d.label}} + +
+
+
+ previous 30 days, same weekday + weekend +
+
+ +
+
+

By type

+ vs previous 30 days +
+ +
+ +
+ {{ty.label}} + +
+ {{ty.count}} + {{ty.delta}} +
+
+
+
+ +
+
+
+

Reliability and impact by service

+ top 11 of 17 services · sorted by {{sortLabel}} +
+ +
+
+
+
+ servicechangesfailure rateincidentsrestoreuser impact +
+ +
+ {{sv.name}}{{sv.owner}} + {{sv.changes}} · {{sv.deploys}} dep + + + {{sv.rate}} + + {{sv.incidents}} {{sv.incDelta}} + {{sv.restore}} + + + + {{sv.impact}} + + + Timeline + · + Simulate + +
+
+
+
+
failure rate = failed deployments ÷ deployments · restore = median incident duration · user impact = time spent by events flagged impact
+
+ +
+
+
+
+

12-month trend

+ may 2025 – apr 2026 +
+ {{trendSentence}} +
+ +
+
+
+ +
+ {{tr.value}} + +
+
+
+
+ + {{tr.month}} + +
+
+
+
+
+
+
+ + + diff --git a/docs/design-system/mockups/search-and-filters.dc.html b/docs/design-system/mockups/search-and-filters.dc.html new file mode 100644 index 0000000..05b25a8 --- /dev/null +++ b/docs/design-system/mockups/search-and-filters.dc.html @@ -0,0 +1,132 @@ + + + + +Tracker — Recherche et filtres + + + + + + + + + + + +
+ +
+

filtrer ce que je regarde · barre de filtre de page

+ +
+ 1 · focus, champ vide +
+ + Filter by env, status, type, team, service — or type to search + / +
+
+
+
recent
+
status:failed env:productionyesterday
+
owner:infra-team -status:donelast week
+
filter by
+
env:production, pré-production…
+
status:failed, running, open…
+
owner:payments-team…
+
impact:true3 events touch users
+
+
+
+ +
+ 2 · une clé tapée, les valeurs proposées avec leur nombre +
+ + status:f + / +
+
+
+
status
+
failed2 in view↵
+
+ +
+
+ +
+ 3 · filtres appliqués, résultat et vue enregistrable +
+ + envproduction, pré-production + -status≠ done + checkout + Clear +
+
+ 1 event · 1 lane · filtered · synced to timeline, calendar + Save as view +
+
+ +
+ 4 · aucun résultat, on propose quoi retirer +
+ No scheduled work matches service:checkout status:failed between 12:00 and 20:00. +
+ try without + status:failed · 1 result + service:checkout · 2 results + Clear all +
+
+
+
+ +
+

aller quelque part · recherche globale ⌘K

+
+ « check » trouve un service, un événement, une page +
+ +
+
+ +
+ syntaxe et raccourcis, communs à toutes les pages +
+
env:production
one value
+
env:production,mco
any of these values
+
-status:done
exclude a value
+
impact:true
events that touch users, whatever their status
+
"schema migration"
exact phrase in titles
+
/
focus the page filter
+
⌘K
search and jump anywhere
+
⌫ on empty field
remove the last filter
+
G then D, T, S
go to dashboard, timeline, streamline
+
+
+
+
+
+ + + diff --git a/docs/design-system/mockups/streamline.dc.html b/docs/design-system/mockups/streamline.dc.html new file mode 100644 index 0000000..91099f3 --- /dev/null +++ b/docs/design-system/mockups/streamline.dc.html @@ -0,0 +1,650 @@ + + + + +Tracker — Streamline + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ + New +
+ +
+
+ +
+
+

Streamline

+ scheduled work across services · tue 07 apr 2026 +
+
+ Today + ← + → + {{rangeLabel}} + +
+
+ + + +
+
+
+ + + {{t.key}}{{t.text}} + + + + Clear + + / +
+ +
+
+ +
+ + +
+ + + {{it.label}} + {{it.hint}} +
+
+
+
+
+ +
+
+
+ + +
+ +
+ {{resultLine}} + filters synced · timeline, streamline, calendar +
+ + +
+ + {{ovHeadline}} + {{ovDetail}} + + + P + Previous + + + Next overlap + N + +
+
+ +
+
+ + + + + +
+
0006121824
+
+ +
+
+
+
+
{{groupHeading}}
+
+ + + + +
+
+ + +
+
+
+ {{sec.name}} + {{sec.count}} +
+
+
+ +
+
+ {{ln.name}} + {{ln.sub}} +
+
+ + + {{lk.label}} + + + + + + + + + + + + {{b.label}} + + + + +
+
+
+
+
+ +
+ No scheduled work matches these filters in this period. + Clear filters +
+
+
+
+
+ +
+ users impacted + overlap + + ←→ pan + −+ zoom + T today + NP overlaps + / filter + GD dashboard +
+
+ + + + + + + + +
+
+ + +
+ +
+ +
+
+
+
+
+ + + diff --git a/docs/design-system/reference/bundle.css b/docs/design-system/reference/bundle.css new file mode 100644 index 0000000..c8077ab --- /dev/null +++ b/docs/design-system/reference/bundle.css @@ -0,0 +1,197 @@ +/* Tracker — component styles. Every value comes from tokens.css. */ +body { margin: 0; font-family: var(--font-sans); color: var(--ink); background: var(--paper); -webkit-font-smoothing: antialiased; } +.tk-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; } +.tk-stack { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-5); } +.tk-inline { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); padding: var(--space-5); } +.tk-panel { background: var(--surface); border: 1px solid var(--rule); border-radius: var(--radius-md); } + +/* Icon */ +.tk-icon { display: inline-block; flex: none; vertical-align: middle; } + +/* Button */ +.tk-btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); height: var(--control-md); padding: 0 14px; border: 1px solid transparent; border-radius: var(--radius-md); font: inherit; font-size: 14px; font-weight: 500; line-height: 1; white-space: nowrap; cursor: pointer; transition: background-color 120ms cubic-bezier(0.2, 0, 0, 1), border-color 120ms cubic-bezier(0.2, 0, 0, 1); } +.tk-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; } +.tk-btn--primary { background: var(--ink); color: var(--on-ink); } +.tk-btn--primary:hover { background: var(--ink-hover); } +.tk-btn--secondary { background: var(--surface); color: var(--ink); border-color: var(--rule-strong); } +.tk-btn--secondary:hover { background: var(--sunken); } +.tk-btn--ghost { background: transparent; color: var(--ink); } +.tk-btn--ghost:hover { background: var(--sunken); } +.tk-btn--sm { height: var(--control-sm); padding: 0 10px; font-size: 13px; } +.tk-btn--icon { width: var(--control-md); padding: 0; } +.tk-btn--icon.tk-btn--sm { width: var(--control-sm); } +.tk-btn[disabled] { opacity: 0.45; cursor: not-allowed; } + +/* Kbd */ +.tk-kbd { display: inline-block; font-family: var(--font-mono); font-size: 11px; line-height: 16px; padding: 0 5px; border: 1px solid var(--rule); border-radius: var(--radius-xs); background: var(--surface); color: var(--muted); } +.tk-btn--primary .tk-kbd { background: transparent; border-color: currentColor; color: inherit; opacity: 0.7; } + +/* Highlight */ +.tk-hl { background: var(--signature); color: var(--on-signature); padding: 1px 5px; border-radius: var(--radius-xs); -webkit-box-decoration-break: clone; box-decoration-break: clone; } +.tk-hl--data { font-family: var(--font-mono); font-size: 11px; line-height: 16px; font-weight: 500; padding: 2px 6px; display: inline-block; } + +/* ImpactMark — magenta marker, same 22 px height as the status tag */ +.tk-impact-mark { display: inline-flex; align-items: center; gap: 4px; height: 22px; box-sizing: border-box; padding: 0 7px 0 5px; border-radius: 4px; background: var(--impact-wash); color: var(--impact); font-family: var(--font-mono); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; } +.tk-impact-mark--compact { width: 22px; padding: 0; justify-content: center; } + +/* StatusMark — tag: glyph 12 px + mono label, 22 px tall */ +.tk-status { display: inline-flex; align-items: center; gap: 5px; height: 22px; box-sizing: border-box; padding: 0 8px 0 6px; border: 1px solid transparent; border-radius: 4px; font-family: var(--font-mono); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; } +.tk-status--failed { background: var(--failed-wash); color: var(--failed); } +.tk-status--warning { background: var(--warning-wash); color: var(--warning); } +.tk-status--running { background: var(--signature); color: var(--on-signature); } +.tk-status--done { border-color: var(--tag-line); color: var(--muted); font-weight: 400; } +.tk-status--open { border-color: var(--tag-line); border-style: dashed; color: var(--muted); font-weight: 400; } +/* Glyph alone (history, legends, ruler): colour by tone */ +.tk-status-glyph { display: inline-flex; align-items: center; } +.tk-tone--failed { color: var(--failed); } +.tk-tone--warning { color: var(--warning); } +.tk-tone--running { color: var(--needle); } +.tk-tone--done, .tk-tone--open { color: var(--muted); } +.tk-glyph { flex: none; display: block; } +.tk-glyph__mark { stroke: var(--on-status); } +.tk-glyph--running { animation: tk-spin 1.6s linear infinite; transform-origin: 50% 50%; } +@keyframes tk-spin { to { transform: rotate(360deg); } } +@media (prefers-reduced-motion: reduce) { .tk-glyph--running { animation: none; } } + +/* Meta line */ +.tk-meta { font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + +/* EventRow */ +.tk-list { display: flex; flex-direction: column; } +.tk-row { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; column-gap: var(--space-3); align-items: start; width: 100%; box-sizing: border-box; padding: var(--row-pad-comfy) var(--space-3); margin: 0; border: 0; border-top: 1px solid var(--rule-soft); background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; transition: background-color 120ms cubic-bezier(0.2, 0, 0, 1); } +.tk-row:hover { background: var(--sunken); } +.tk-row:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; } +.tk-row[aria-selected="true"] { background: var(--signature-wash); } +.tk-row__time { font-family: var(--font-mono); font-size: 12px; line-height: 19px; color: var(--muted); font-variant-numeric: tabular-nums; } +.tk-row__body { min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); } +.tk-row__title { font-size: 14px; line-height: 19px; font-weight: 500; color: var(--ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; } +.tk-row__status { justify-self: end; display: flex; align-items: center; gap: 6px; height: 19px; } +.tk-row--compact { padding-top: var(--row-pad-compact); padding-bottom: var(--row-pad-compact); } +.tk-row--compact .tk-row__body { flex-direction: row; align-items: baseline; gap: var(--space-3); } +.tk-row--compact .tk-row__title { white-space: nowrap; flex: 0 1 auto; } +.tk-row--compact .tk-meta { flex: 1 1 0; } + +/* MetricStrip */ +.tk-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); background: var(--sunken); border-radius: var(--radius-md); } +.tk-metric { padding: 14px var(--space-4); display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; } +.tk-metric + .tk-metric { border-left: 1px solid var(--rule); } +.tk-metric__label { font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--muted); } +.tk-metric__value { font-family: var(--font-mono); font-size: 30px; line-height: 34px; font-weight: 500; letter-spacing: -0.04em; font-variant-numeric: tabular-nums; color: var(--ink); } +.tk-metric__value--failed { color: var(--failed); } +.tk-metric__value--warning { color: var(--warning); } +.tk-metric__value--impact { color: var(--impact); } +.tk-metric__delta { display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--muted); } +.tk-metric__delta--worse { color: var(--warning); font-weight: 500; } +.tk-metric__sub { font-size: 12px; line-height: 16px; color: var(--muted); } + +/* SegmentedControl */ +.tk-seg { display: inline-flex; padding: 3px; gap: 2px; border-radius: 7px; background: var(--sunken); } +.tk-seg__item { height: var(--control-sm); padding: 0 12px; border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent; color: var(--muted); font-family: var(--font-mono); font-size: 12px; cursor: pointer; } +.tk-seg__item:hover { color: var(--ink); } +.tk-seg__item:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; } +.tk-seg__item[aria-pressed="true"] { background: var(--surface); border-color: var(--rule); color: var(--ink); font-weight: 500; } + +/* FilterBar */ +.tk-filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); } +.tk-filters__spacer { flex: 1 1 auto; } +.tk-filters__scope { font-family: var(--font-mono); font-size: 11px; color: var(--muted); } +.tk-token { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 3px 0 10px; border: 1px solid var(--rule-strong); border-radius: var(--radius-sm); background: var(--surface); font-family: var(--font-mono); font-size: 12px; color: var(--ink); } +.tk-token__key { color: var(--muted); } +.tk-token__remove { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; border: 0; border-radius: var(--radius-xs); background: transparent; color: var(--muted); cursor: pointer; } +.tk-token__remove:hover { background: var(--sunken); color: var(--ink); } +.tk-token__remove:focus-visible { outline: 2px solid var(--focus-ring); } + +/* Nav */ +.tk-sidebar { width: var(--sidebar-w); box-sizing: border-box; background: var(--surface); border-right: 1px solid var(--rule); padding: 18px 14px 24px; display: flex; flex-direction: column; gap: 22px; } +.tk-nav { display: flex; flex-direction: column; gap: 18px; } +.tk-nav__group { display: flex; flex-direction: column; gap: 2px; } +.tk-nav__label { font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--muted); padding: 0 10px 6px; } +.tk-nav__item { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: var(--radius-sm); font-size: 14px; line-height: 20px; color: var(--ink); text-decoration: none; } +.tk-nav__item .tk-icon { color: var(--muted); } +.tk-nav__item:hover { background: var(--sunken); } +.tk-nav__item:hover .tk-icon { color: var(--ink); } +.tk-nav__item:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; } +.tk-nav__item[aria-current="page"] { background: var(--signature); color: var(--on-signature); font-weight: 600; } +.tk-nav__item[aria-current="page"] .tk-icon { color: var(--on-signature); } +.tk-nav__count { margin-left: auto; font-family: var(--font-mono); font-size: 11px; color: inherit; opacity: 0.75; } +.tk-sidebar__foot { margin-top: auto; display: flex; } +.tk-sidebar--collapsed { width: 60px; padding: 16px 10px 16px; align-items: center; gap: 18px; } +.tk-sidebar--collapsed .tk-nav { gap: 10px; align-items: center; } +.tk-sidebar--collapsed .tk-nav__label { display: none; } +.tk-sidebar--collapsed .tk-nav__group { align-items: center; } +.tk-sidebar--collapsed .tk-nav__group + .tk-nav__group { border-top: 1px solid var(--rule); padding-top: 10px; } +.tk-sidebar--collapsed .tk-nav__item { position: relative; justify-content: center; width: 40px; height: 36px; padding: 0; box-sizing: border-box; } +.tk-sidebar--collapsed .tk-nav__count { position: absolute; top: 3px; right: 3px; margin: 0; min-width: 14px; height: 14px; padding: 0 3px; box-sizing: border-box; border-radius: 7px; background: var(--ink); color: var(--on-ink); font-size: 9px; line-height: 14px; text-align: center; opacity: 1; } +.tk-sidebar--collapsed .tk-sidebar__foot { justify-content: center; } + +/* TimeRuler */ +.tk-ruler { display: block; width: 100%; height: 40px; overflow: visible; } +.tk-ruler__grid { stroke: var(--grid); } +.tk-ruler__major { stroke: var(--ink); } +.tk-ruler__label { fill: var(--muted); font-family: var(--font-mono); font-size: 9.5px; } +.tk-ruler__mark--failed { fill: var(--failed); } +.tk-ruler__mark--warning { fill: var(--warning); } +.tk-ruler__mark--running { fill: var(--needle); } +.tk-ruler__mark--done { fill: none; stroke: var(--muted); stroke-width: 1.2; } +.tk-ruler__mark--open { fill: none; stroke: var(--muted); stroke-width: 1.2; stroke-dasharray: 2 1.5; } +.tk-ruler__needle { stroke: var(--needle); stroke-width: 1.5; } +.tk-ruler__chip { fill: var(--signature); } +.tk-ruler__chip-text { fill: var(--on-signature); font-family: var(--font-mono); font-size: 9.5px; font-weight: 500; } + +/* CommandPalette */ +.tk-palette { width: 100%; max-width: 640px; box-sizing: border-box; background: var(--overlay); border: 1px solid var(--rule); border-radius: var(--radius-lg); box-shadow: var(--shadow-overlay); overflow: hidden; } +.tk-palette__search { display: flex; align-items: center; gap: 10px; height: 52px; padding: 0 var(--space-4); border-bottom: 1px solid var(--rule); color: var(--muted); } +.tk-palette__input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; font: inherit; font-size: 16px; color: var(--ink); } +.tk-palette__input::placeholder { color: var(--muted); } +.tk-palette__list { padding: 6px 0; max-height: 360px; overflow-y: auto; } +.tk-palette__group { font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--muted); padding: 10px var(--space-4) 4px; } +.tk-palette__item { display: flex; align-items: center; gap: 10px; padding: 8px var(--space-4); font-size: 14px; color: var(--ink); cursor: pointer; } +.tk-palette__item .tk-icon { color: var(--muted); } +.tk-palette__item[aria-selected="true"] { background: var(--signature-wash); } +.tk-palette__item[aria-selected="true"] .tk-icon { color: var(--ink); } +.tk-palette__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.tk-palette__hint { font-family: var(--font-mono); font-size: 11px; color: var(--muted); white-space: nowrap; } +.tk-palette__footer { display: flex; flex-wrap: wrap; gap: var(--space-4); padding: 10px var(--space-4); border-top: 1px solid var(--rule); font-family: var(--font-mono); font-size: 11px; color: var(--muted); } + +/* EventDrawer */ +.tk-drawer { width: var(--drawer-w); max-width: 100%; box-sizing: border-box; background: var(--overlay); border-left: 1px solid var(--rule); box-shadow: var(--shadow-overlay); display: flex; flex-direction: column; } +.tk-drawer__head { padding: var(--space-5) var(--space-6) var(--space-4); border-bottom: 1px solid var(--rule); display: flex; flex-direction: column; gap: var(--space-2); } +.tk-drawer__bar { display: flex; align-items: center; gap: var(--space-3); } +.tk-drawer__bar .tk-btn { margin-left: auto; } +.tk-drawer__title { margin: 0; font-size: 19px; line-height: 26px; font-weight: 600; letter-spacing: -0.02em; } +.tk-drawer__head--band { background: var(--sunken); gap: var(--space-3); padding-bottom: var(--space-4); } +.tk-type { display: inline-flex; align-items: center; gap: 6px; height: 22px; box-sizing: border-box; padding: 0 8px 0 6px; border: 1px solid var(--rule-strong); border-radius: 4px; background: var(--surface); font-family: var(--font-mono); font-size: 11px; font-weight: 500; color: var(--ink); white-space: nowrap; } +.tk-chips { display: flex; flex-wrap: wrap; gap: 6px; } +.tk-chip { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; height: 28px; box-sizing: border-box; padding: 0 10px; border: 1px solid var(--rule-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer; transition: border-color 120ms cubic-bezier(0.2, 0, 0, 1); } +.tk-chip .tk-icon { color: var(--muted); } +.tk-chip:hover { border-color: var(--ink); } +.tk-chip:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; } +.tk-chip__text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.tk-chip--quiet { border-color: var(--rule); background: transparent; color: var(--muted); font-family: var(--font-mono); font-size: 12px; font-weight: 400; cursor: default; } +.tk-chip--quiet:hover { border-color: var(--rule); } +.tk-links { display: flex; flex-wrap: wrap; gap: var(--space-2); } +.tk-linkbtn { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; height: 30px; box-sizing: border-box; padding: 0 10px; border: 1px solid var(--rule-strong); border-radius: var(--radius-md); background: var(--surface); color: var(--ink); font-family: var(--font-mono); font-size: 12px; font-weight: 500; text-decoration: none; white-space: nowrap; transition: border-color 120ms cubic-bezier(0.2, 0, 0, 1); } +.tk-linkbtn:hover { border-color: var(--ink); } +.tk-linkbtn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; } +.tk-linkbtn__ext { display: inline-flex; color: var(--muted); } +.tk-linkbtn--empty { border-style: dashed; border-color: var(--tag-line); background: transparent; color: var(--muted); font-weight: 400; padding-right: 2px; } +.tk-linkbtn--empty:hover { border-color: var(--tag-line); } +.tk-linkbtn--empty .tk-btn { height: 24px; width: 24px; } +.tk-impact { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 12px var(--space-6); background: var(--impact); color: var(--on-impact); } +.tk-impact__title { font-size: 14px; line-height: 20px; font-weight: 600; } +.tk-impact__note { font-family: var(--font-mono); font-size: 11px; line-height: 16px; opacity: 0.85; } +.tk-impact--none { gap: 8px; padding: 10px var(--space-6); background: transparent; color: var(--muted); font-size: 13px; border-bottom: 1px solid var(--rule-soft); } +.tk-drawer__body { padding: var(--space-4) var(--space-6) var(--space-6); display: flex; flex-direction: column; gap: var(--space-6); overflow-y: auto; } +.tk-drawer__section-label { font-family: var(--font-mono); font-size: 11px; color: var(--muted); margin: 0 0 var(--space-2); font-weight: 400; } +.tk-fields { display: grid; grid-template-columns: 112px minmax(0, 1fr); margin: 0; } +.tk-fields dt { font-family: var(--font-mono); font-size: 11px; line-height: 20px; color: var(--muted); padding: 6px 0; border-top: 1px solid var(--rule-soft); } +.tk-fields dd { margin: 0; font-family: var(--font-mono); font-size: 12px; line-height: 20px; color: var(--ink); padding: 6px 0; border-top: 1px solid var(--rule-soft); min-width: 0; overflow-wrap: anywhere; } +.tk-fields a { color: var(--ink); text-underline-offset: 3px; } +.tk-history { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; } +.tk-history li { display: grid; grid-template-columns: 64px 12px minmax(0, 1fr); column-gap: var(--space-3); align-items: center; padding: 6px 0; font-size: 13px; line-height: 19px; } +.tk-history time { font-family: var(--font-mono); font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; } +.tk-drawer__foot { margin-top: auto; padding: var(--space-4) var(--space-6); border-top: 1px solid var(--rule); display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); } + +/* Logo */ +.tk-logo { display: inline-flex; align-items: center; gap: 10px; color: var(--ink); text-decoration: none; } +.tk-logo__word { font-family: var(--font-sans); font-weight: 600; letter-spacing: -0.035em; } diff --git a/docs/design-system/reference/bundle.js b/docs/design-system/reference/bundle.js new file mode 100644 index 0000000..ed899ef --- /dev/null +++ b/docs/design-system/reference/bundle.js @@ -0,0 +1,433 @@ +/* @ds-bundle: {"format":4,"namespace":"Tracker","components":[{"name":"Logo"},{"name":"Icon"},{"name":"Button"},{"name":"Kbd"},{"name":"Highlight"},{"name":"ImpactMark"},{"name":"StatusMark"},{"name":"EventRow"},{"name":"MetricStrip"},{"name":"SegmentedControl"},{"name":"FilterBar"},{"name":"Sidebar"},{"name":"TimeRuler"},{"name":"CommandPalette"},{"name":"EventDrawer"}]} */ +(function () { + var React = window.React; + var h = React.createElement; + var Fragment = React.Fragment; + + function cx() { + var out = []; + for (var i = 0; i < arguments.length; i++) { if (arguments[i]) out.push(arguments[i]); } + return out.join(' '); + } + function omit(obj, keys) { + var out = {}; + for (var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k) && keys.indexOf(k) === -1) out[k] = obj[k]; } + return out; + } + var idCounter = 0; + function useStableId(prefix) { + var ref = React.useRef(null); + if (ref.current === null) { idCounter += 1; ref.current = prefix + idCounter; } + return ref.current; + } + + /* ---------- Icons: 16 px grid, 1.5 px stroke, square caps, mitre joins ---------- */ + var icons = { + dashboard: ['M2.75 11.75 A5.25 5.25 0 0 1 13.25 11.75', 'M8 11.75 L10.75 7', 'M1.75 13.75 H14.25'], + timeline: ['M4 2.75 V13.25', 'M4 4.75 H6', 'M4 8 H6', 'M4 11.25 H6', 'M8.5 4.75 H13.25', 'M8.5 8 H12', 'M8.5 11.25 H13.25'], + streamline: ['M2.75 4.25 H8.25', 'M5.25 8 H12.75', 'M3.75 11.75 H9.75', 'M11 2 V14'], + calendar: ['M2.75 3.75 H13.25 V13.25 H2.75 Z', 'M2.75 6.75 H13.25', 'M5.5 2 V5', 'M10.5 2 V5', 'M5.25 9.75 H7.25'], + overlaps: ['M2.75 6 H10.25', 'M5.75 10 H13.25', 'M5.75 3.75 V12.25', 'M10.25 3.75 V12.25'], + insights: ['M2.75 13.25 H13.25', 'M4.75 10.5 V13.25', 'M8 7.5 V13.25', 'M11.25 4.5 V13.25'], + catalog: ['M2.75 2.75 H13.25 V13.25 H2.75 Z', 'M2.75 6.25 H13.25', 'M2.75 9.75 H13.25', 'M7 4.5 H9', 'M7 8 H9', 'M7 11.5 H9'], + architecture: ['M2.75 2.75 H6.25 V6.25 H2.75 Z', 'M9.75 2.75 H13.25 V6.25 H9.75 Z', 'M6.25 9.75 H9.75 V13.25 H6.25 Z', 'M4.5 6.25 V8 H11.5 V6.25', 'M8 8 V9.75'], + compliance: ['M3.75 2.75 H12.25 V13.25 H3.75 Z', 'M5.75 8.25 L7.25 9.75 L10.25 6.25'], + drifts: ['M1.75 11 H5.5 L10.5 5 H14.25', { d: 'M10.5 11 H14.25', dash: '1.5 1.5', cap: 'butt' }], + rpa: ['M12.25 9.5 A4.5 4.5 0 1 1 11.6 4.9', 'M12 2.25 V5.25 H9'], + locks: ['M4.25 7.25 H11.75 V13.25 H4.25 Z', 'M5.75 7.25 V5 A2.25 2.25 0 0 1 10.25 5 V7.25', 'M8 9.5 V11'], + links: ['M7 9 L13.25 2.75', 'M9 2.75 H13.25 V7', 'M11.25 9.5 V13.25 H2.75 V4.75 H6.5'], + docs: ['M3.75 2.75 H9.5 L12.25 5.5 V13.25 H3.75 Z', 'M9.5 2.75 V5.5 H12.25', 'M5.75 8.25 H10.25', 'M5.75 10.75 H10.25'], + deployment: ['M8 10.75 V2.75', 'M4.75 6 L8 2.75 L11.25 6', 'M2.75 13.25 H13.25'], + operation: ['M2.75 5 H13.25', 'M2.75 11 H13.25', 'M6 3.25 V6.75', 'M10 9.25 V12.75'], + incident: ['M1.75 8.5 H4.75 L6.5 3.75 L9.5 12.25 L11.25 8.5 H14.25'], + service: ['M2.75 3.25 H13.25 V7.25 H2.75 Z', 'M2.75 8.75 H13.25 V12.75 H2.75 Z', 'M5 5.25 H6', 'M5 10.75 H6'], + now: ['M8 1.75 V14.25', 'M5.75 1.75 H10.25'], + rows: ['M2.75 3.5 H13.25', 'M2.75 6.5 H13.25', 'M2.75 9.5 H13.25', 'M2.75 12.5 H13.25'], + search: ['M11.25 7 A4.25 4.25 0 1 1 2.75 7 A4.25 4.25 0 1 1 11.25 7 Z', 'M10.25 10.25 L13.5 13.5'], + filter: ['M2.75 4.5 H13.25', 'M4.75 8 H11.25', 'M6.75 11.5 H9.25'], + plus: ['M8 3 V13', 'M3 8 H13'], + close: ['M4 4 L12 12', 'M12 4 L4 12'], + 'chevron-down': ['M4.5 6.25 L8 9.75 L11.5 6.25'], + 'arrow-right': ['M2.75 8 H13', 'M9.5 4.5 L13 8 L9.5 11.5'], + copy: ['M5.75 5.75 H13.25 V13.25 H5.75 Z', 'M10.25 5.75 V2.75 H2.75 V10.25 H5.75'], + environment: ['M2.75 5.25 L8 2.75 L13.25 5.25 L8 7.75 Z', 'M2.75 8.25 L8 10.75 L13.25 8.25', 'M2.75 11.25 L8 13.75 L13.25 11.25'], + owner: ['M10.25 5 A2.25 2.25 0 1 1 5.75 5 A2.25 2.25 0 1 1 10.25 5 Z', 'M3.25 13.25 V12.25 A3 3 0 0 1 6.25 9.25 H9.75 A3 3 0 0 1 12.75 12.25 V13.25'], + impact: ['M9.75 8 A1.75 1.75 0 1 1 6.25 8 A1.75 1.75 0 1 1 9.75 8 Z', 'M4 12 A5.66 5.66 0 0 1 4 4', 'M12 4 A5.66 5.66 0 0 1 12 12'], + 'pull-request': ['M6.25 3.5 A1.75 1.75 0 1 1 2.75 3.5 A1.75 1.75 0 1 1 6.25 3.5 Z', 'M4.5 5.25 V13.25', 'M13.25 12.5 A1.75 1.75 0 1 1 9.75 12.5 A1.75 1.75 0 1 1 13.25 12.5 Z', 'M11.5 10.75 V4.75 H7.75', 'M9.25 3.25 L7.75 4.75 L9.25 6.25'], + ticket: ['M2.75 4.25 H13.25 V6.5 A1.5 1.5 0 0 0 13.25 9.5 V11.75 H2.75 V9.5 A1.5 1.5 0 0 0 2.75 6.5 Z', 'M9.75 5.75 V6.75 M9.75 7.75 V8.25 M9.75 9.25 V10.25'] + }; + + function Icon(props) { + var size = props.size || 16; + var paths = icons[props.name] || []; + var a11y = props.title ? { role: 'img', 'aria-label': props.title } : { 'aria-hidden': 'true' }; + return h('svg', Object.assign({ className: cx('tk-icon', props.className), width: size, height: size, viewBox: '0 0 16 16', fill: 'none', stroke: 'currentColor', strokeWidth: 1.5, strokeLinecap: 'square', strokeLinejoin: 'miter' }, a11y), + paths.map(function (p, i) { + if (typeof p === 'string') return h('path', { key: i, d: p }); + return h('path', { key: i, d: p.d, strokeDasharray: p.dash, strokeLinecap: p.cap || 'square' }); + })); + } + + /* ---------- Logo ---------- */ + function Logo(props) { + var size = props.size || 24; + var showWord = props.word !== false; + return h('span', { className: 'tk-logo', role: 'img', 'aria-label': 'Tracker' }, + h('svg', { width: size, height: size, viewBox: '0 0 44 44', 'aria-hidden': 'true' }, + h('rect', { x: 0, y: 0, width: 44, height: 44, rx: 8, style: { fill: 'var(--ink)' } }), + h('rect', { x: 19, y: 12, width: 6, height: 22, rx: 1, style: { fill: 'var(--surface)' } }), + h('rect', { x: 9, y: 11, width: 26, height: 8, rx: 1.5, style: { fill: 'var(--signature)' } })), + showWord ? h('span', { className: 'tk-logo__word', 'aria-hidden': 'true', style: { fontSize: Math.round(size * 0.78) + 'px' } }, 'tracker') : null); + } + + /* ---------- Button, Kbd, Highlight ---------- */ + function Button(props) { + var variant = props.variant || 'secondary'; + var size = props.size || 'md'; + var iconOnly = props.icon && !props.children; + var rest = omit(props, ['variant', 'size', 'icon', 'iconRight', 'className', 'children', 'type']); + return h('button', Object.assign({ type: props.type || 'button', className: cx('tk-btn', 'tk-btn--' + variant, size === 'sm' && 'tk-btn--sm', iconOnly && 'tk-btn--icon', props.className) }, rest), + props.icon ? h(Icon, { name: props.icon }) : null, + props.children, + props.iconRight ? h(Icon, { name: props.iconRight, size: 12 }) : null); + } + + function Kbd(props) { + return h('kbd', { className: 'tk-kbd' }, props.children); + } + + /* ImpactMark: the event touches users (attribute impact = true). Magenta, independent of status. */ + function ImpactMark(props) { + var label = props.title || 'Users impacted'; + if (props.compact) { + return h('span', { className: cx('tk-impact-mark', 'tk-impact-mark--compact', props.className), role: 'img', 'aria-label': label, title: label }, + h(Icon, { name: 'impact', size: 12 })); + } + return h('span', { className: cx('tk-impact-mark', props.className), title: label }, + h(Icon, { name: 'impact', size: 12 }), + h('span', null, props.text || 'impact')); + } + + function Highlight(props) { + return h('span', { className: cx('tk-hl', props.data && 'tk-hl--data', props.className) }, props.children); + } + + /* ---------- StatusMark ---------- */ + /* Status glyphs: 16 px grid, round caps (distinct from the square-capped icon set). + failed = square + ✕, warning = triangle + !, running = open arc + dot, done = circle + ✓, open = empty circle. */ + var STATUSES = ['failed', 'warning', 'running', 'done', 'open']; + function Glyph(props) { + var s = props.status; + var size = props.size || 12; + var common = { className: cx('tk-glyph', s === 'running' && 'tk-glyph--running'), width: size, height: size, viewBox: '0 0 16 16', 'aria-hidden': 'true' }; + var mark = { fill: 'none', className: 'tk-glyph__mark', strokeWidth: 1.75, strokeLinecap: 'round' }; + if (s === 'failed') return h('svg', common, + h('rect', { x: 1, y: 1, width: 14, height: 14, rx: 3, fill: 'currentColor' }), + h('path', Object.assign({ d: 'M5.6 5.6 L10.4 10.4 M10.4 5.6 L5.6 10.4' }, mark))); + if (s === 'warning') return h('svg', common, + h('path', { d: 'M8 1.2 L15.1 14.4 L0.9 14.4 Z', fill: 'currentColor', stroke: 'currentColor', strokeWidth: 0.8, strokeLinejoin: 'round' }), + h('path', Object.assign({ d: 'M8 6.3 V9.4 M8 11.9 V12' }, mark))); + if (s === 'running') return h('svg', common, + h('path', { d: 'M8 1.75 A6.25 6.25 0 1 1 1.75 8', fill: 'none', stroke: 'currentColor', strokeWidth: 1.75, strokeLinecap: 'round' }), + h('circle', { cx: 8, cy: 8, r: 2.1, fill: 'currentColor' })); + if (s === 'open') return h('svg', common, + h('circle', { cx: 8, cy: 8, r: 6.25, fill: 'none', stroke: 'currentColor', strokeWidth: 1.5 })); + return h('svg', common, + h('circle', { cx: 8, cy: 8, r: 6.25, fill: 'none', stroke: 'currentColor', strokeWidth: 1.5 }), + h('path', { d: 'M5.1 8.2 L7.1 10.2 L10.9 6.1', fill: 'none', stroke: 'currentColor', strokeWidth: 1.5, strokeLinecap: 'round', strokeLinejoin: 'round' })); + } + + function StatusMark(props) { + var s = STATUSES.indexOf(props.status) === -1 ? 'done' : props.status; + var text = props.text || s; + if (props.label === false) { + return h('span', { className: cx('tk-status-glyph', 'tk-tone--' + s, props.className), role: 'img', 'aria-label': text }, + h(Glyph, { status: s, size: props.size || 14 })); + } + return h('span', { className: cx('tk-status', 'tk-status--' + s, props.className) }, + h(Glyph, { status: s, size: 12 }), + h('span', null, text)); + } + + /* ---------- EventRow ---------- */ + function metaText(meta) { + if (!meta) return ''; + return Array.isArray(meta) ? meta.filter(Boolean).join(' · ') : meta; + } + + function EventRow(props) { + var compact = props.density === 'compact'; + var s = props.status || 'done'; + function onKey(e) { + if ((e.key === 'Enter' || e.key === ' ') && props.onSelect) { e.preventDefault(); props.onSelect(); } + } + return h('div', { role: 'option', tabIndex: 0, 'aria-selected': props.selected ? 'true' : 'false', className: cx('tk-row', compact && 'tk-row--compact'), onClick: props.onSelect, onKeyDown: onKey }, + h('span', { className: 'tk-row__time' }, props.time), + h('span', { className: 'tk-row__body' }, + h('span', { className: 'tk-row__title' }, props.title), + h('span', { className: 'tk-meta' }, metaText(props.meta))), + h('span', { className: 'tk-row__status' }, + props.impact ? h(ImpactMark, { compact: true }) : null, + h(StatusMark, { status: s }))); + } + + function EventList(props) { + var events = props.events || []; + return h('div', { role: 'listbox', 'aria-label': props.label || 'Events', className: 'tk-list' }, + events.map(function (ev, i) { + return h(EventRow, Object.assign({ key: ev.id || i, density: props.density, selected: props.selectedId != null && ev.id === props.selectedId, onSelect: props.onSelect ? function () { props.onSelect(ev); } : undefined }, ev)); + })); + } + + /* ---------- MetricStrip ---------- */ + function MetricStrip(props) { + var items = props.items || []; + return h('div', { className: 'tk-metrics', role: 'group', 'aria-label': props.label || 'Metrics' }, + items.map(function (m, i) { + return h('div', { key: i, className: 'tk-metric' }, + h('span', { className: 'tk-metric__label' }, m.label), + h('span', { className: cx('tk-metric__value', m.tone && 'tk-metric__value--' + m.tone) }, m.value), + m.sub ? h('span', { className: 'tk-metric__sub' }, m.sub) : null, + m.delta ? h('span', { className: cx('tk-metric__delta', 'tk-metric__delta--' + (m.trend || 'flat')), title: m.trend === 'worse' ? 'Worse than the previous period' : (m.trend === 'better' ? 'Better than the previous period' : 'Compared with the previous period') }, + m.trend === 'worse' ? h(Glyph, { status: 'warning', size: 10 }) : null, + h('span', null, m.delta)) : null); + })); + } + + /* ---------- SegmentedControl ---------- */ + function SegmentedControl(props) { + var options = props.options || []; + var controlled = props.value !== undefined; + var st = React.useState(props.defaultValue || options[0]); + var value = controlled ? props.value : st[0]; + function pick(o) { if (!controlled) st[1](o); if (props.onChange) props.onChange(o); } + return h('div', { className: 'tk-seg', role: 'group', 'aria-label': props.label || 'Options' }, + options.map(function (o) { + return h('button', { key: o, type: 'button', className: 'tk-seg__item', 'aria-pressed': o === value ? 'true' : 'false', onClick: function () { pick(o); } }, o); + })); + } + + /* ---------- FilterBar ---------- */ + function FilterBar(props) { + var init = props.filters || []; + var st = React.useState(init); + var filters = props.onRemove ? init : st[0]; + function remove(i) { + if (props.onRemove) props.onRemove(filters[i], i); + else st[1](filters.filter(function (_, j) { return j !== i; })); + } + return h('div', { className: 'tk-filters', role: 'toolbar', 'aria-label': 'Filters' }, + h(SegmentedControl, { label: 'Period', options: props.periods || ['24h', '7d', '30d'], defaultValue: props.period || (props.periods || ['24h', '7d', '30d'])[1], onChange: props.onPeriodChange }), + filters.map(function (f, i) { + return h('span', { key: f.key + ':' + f.value, className: 'tk-token' }, + h('span', { className: 'tk-token__key' }, f.key), + h('span', null, f.value), + h('button', { type: 'button', className: 'tk-token__remove', 'aria-label': 'Remove filter ' + f.key + ' ' + f.value, onClick: function () { remove(i); } }, h(Icon, { name: 'close', size: 12 }))); + }), + h(Button, { variant: 'ghost', size: 'sm', icon: 'plus', onClick: props.onAdd }, 'Filter'), + h('span', { className: 'tk-filters__spacer' }), + props.scope ? h('span', { className: 'tk-filters__scope' }, props.scope) : null); + } + + /* ---------- Sidebar ---------- */ + function Sidebar(props) { + var groups = props.groups || []; + var collapsed = !!props.collapsed; + return h('aside', { className: cx('tk-sidebar', collapsed && 'tk-sidebar--collapsed') }, + props.logo === false ? null : h('a', { href: props.homeHref || '#', className: 'tk-logo', 'aria-label': 'Tracker home', style: { padding: collapsed ? '0' : '2px 6px' } }, h(Logo, { size: collapsed ? 28 : 26, word: !collapsed })), + h('nav', { className: 'tk-nav', 'aria-label': 'Main' }, + groups.map(function (g) { + return h('div', { key: g.label, className: 'tk-nav__group', role: collapsed ? 'group' : undefined, 'aria-label': collapsed ? g.label : undefined }, + h('span', { className: 'tk-nav__label' }, g.label), + g.items.map(function (it) { + var current = it.name === props.active; + var hint = it.count != null ? it.name + ' (' + it.count + ')' : it.name; + return h('a', { key: it.name, href: it.href || '#', className: 'tk-nav__item', 'aria-current': current ? 'page' : undefined, 'aria-label': collapsed ? hint : undefined, title: collapsed ? hint : undefined }, + it.icon ? h(Icon, { name: it.icon }) : null, + collapsed ? null : h('span', null, it.name), + it.count != null ? h('span', { className: 'tk-nav__count' }, it.count) : null); + })); + })), + props.onToggle ? h('div', { className: 'tk-sidebar__foot' }, + h(Button, { variant: 'ghost', size: 'sm', icon: collapsed ? 'arrow-right' : 'rows', 'aria-label': collapsed ? 'Expand navigation' : 'Collapse navigation', onClick: props.onToggle }, collapsed ? null : 'Collapse')) : null); + } + + /* ---------- TimeRuler ---------- */ + function toHours(t) { + if (typeof t === 'number') return t; + var p = String(t).split(':'); + return parseInt(p[0], 10) + (parseInt(p[1] || '0', 10) / 60); + } + function TimeRuler(props) { + /* startHour / endHour are the canonical props; from / to stay as aliases (from is reserved on x-import). */ + var from = props.startHour != null ? Number(props.startHour) : (props.from != null ? Number(props.from) : 12); + var to = props.endHour != null ? Number(props.endHour) : (props.to != null ? Number(props.to) : 18); + var W = props.width || 600; + var H = 40; + var pph = W / (to - from); + var gid = useStableId('tk-ruler-grid-'); + function x(t) { return (toHours(t) - from) * pph; } + var majors = []; + for (var hr = from; hr <= to; hr++) majors.push(hr); + var marks = props.marks || []; + var nowX = props.now ? x(props.now) : null; + var nowLabel = props.now ? 'now ' + props.now : ''; + var chipW = nowLabel.length * 6.2 + 12; + var chipX = nowX != null ? Math.min(nowX, W - chipW) : 0; + return h('svg', { className: 'tk-ruler', viewBox: '0 0 ' + W + ' ' + H, preserveAspectRatio: 'none', role: 'img', 'aria-label': props.label || ('Time axis from ' + from + ':00 to ' + to + ':00' + (props.now ? ', now ' + props.now : '')) }, + h('defs', null, h('pattern', { id: gid, width: 8, height: 8, patternUnits: 'userSpaceOnUse' }, h('path', { d: 'M8 0.5 H0.5 V8', fill: 'none', className: 'tk-ruler__grid', strokeWidth: 1 }))), + h('rect', { x: 0, y: 0, width: W, height: H, rx: 4, fill: 'url(#' + gid + ')' }), + majors.map(function (m, i) { + var mx = (m - from) * pph + 0.5; + return h(Fragment, { key: m }, + h('line', { x1: mx, y1: 26, x2: mx, y2: H, className: 'tk-ruler__major', strokeWidth: 1 }), + i < majors.length - 1 ? h('text', { x: mx + 4.5, y: 34, className: 'tk-ruler__label' }, (m < 10 ? '0' : '') + m + ':00') : null); + }), + marks.map(function (mk, i) { + return h('circle', { key: i, cx: x(mk.at), cy: 14, r: 3, className: 'tk-ruler__mark--' + (mk.status || 'done') }); + }), + nowX != null ? h(Fragment, null, + h('rect', { x: chipX, y: 4, width: chipW, height: 16, rx: 3, className: 'tk-ruler__chip' }), + h('text', { x: chipX + 6, y: 15.5, className: 'tk-ruler__chip-text' }, nowLabel), + h('line', { x1: nowX, y1: 4, x2: nowX, y2: H, className: 'tk-ruler__needle' })) : null); + } + + /* ---------- CommandPalette ---------- */ + function CommandPalette(props) { + var qs = React.useState(props.defaultQuery || ''); + var as = React.useState(0); + var q = qs[0].trim().toLowerCase(); + var groups = (props.groups || []).map(function (g) { + return { label: g.label, items: g.items.filter(function (it) { return !q || it.label.toLowerCase().indexOf(q) !== -1 || (it.hint || '').toLowerCase().indexOf(q) !== -1; }) }; + }).filter(function (g) { return g.items.length; }); + var flat = []; + groups.forEach(function (g) { g.items.forEach(function (it) { flat.push(it); }); }); + var active = Math.min(as[0], Math.max(flat.length - 1, 0)); + var listId = useStableId('tk-palette-list-'); + function onKey(e) { + if (e.key === 'ArrowDown') { e.preventDefault(); as[1](Math.min(active + 1, flat.length - 1)); } + else if (e.key === 'ArrowUp') { e.preventDefault(); as[1](Math.max(active - 1, 0)); } + else if (e.key === 'Enter' && flat[active] && props.onSelect) { e.preventDefault(); props.onSelect(flat[active]); } + else if (e.key === 'Escape' && props.onClose) { props.onClose(); } + } + var idx = -1; + return h('div', { className: 'tk-palette', role: 'dialog', 'aria-label': 'Command palette' }, + h('div', { className: 'tk-palette__search' }, + h(Icon, { name: 'search' }), + h('input', { className: 'tk-palette__input', type: 'text', role: 'combobox', 'aria-expanded': 'true', 'aria-controls': listId, 'aria-activedescendant': flat.length ? listId + '-' + active : undefined, 'aria-label': 'Search or run a command', placeholder: props.placeholder || 'Search events, services, teams — or type a command', value: qs[0], onChange: function (e) { qs[1](e.target.value); as[1](0); }, onKeyDown: onKey, autoFocus: props.autoFocus }), + h(Kbd, null, 'esc')), + h('div', { className: 'tk-palette__list', role: 'listbox', id: listId }, + groups.length === 0 ? h('div', { className: 'tk-palette__group' }, 'No results for “' + qs[0] + '”') : null, + groups.map(function (g) { + return h('div', { key: g.label, role: 'group', 'aria-label': g.label }, + h('div', { className: 'tk-palette__group', 'aria-hidden': 'true' }, g.label), + g.items.map(function (it) { + idx += 1; + var my = idx; + return h('div', { key: it.label, id: listId + '-' + my, role: 'option', 'aria-selected': my === active ? 'true' : 'false', className: 'tk-palette__item', onMouseEnter: function () { as[1](my); }, onClick: function () { if (props.onSelect) props.onSelect(it); } }, + it.icon ? h(Icon, { name: it.icon }) : null, + h('span', { className: 'tk-palette__label' }, it.label), + it.hint ? h('span', { className: 'tk-palette__hint' }, it.hint) : null, + it.kbd ? h(Kbd, null, it.kbd) : null); + })); + })), + h('div', { className: 'tk-palette__footer', 'aria-hidden': 'true' }, + h('span', null, '↑ ↓ move'), h('span', null, '↵ open'), h('span', null, 'esc close'))); + } + + /* ---------- EventDrawer ---------- */ + /* Bandeau layout: type, status, title, then the facts people act on (service, environment, owner, + pull request, ticket) in the header band; user impact as its own full-width band in --impact. */ + var TYPE_ICON = { deployment: 'deployment', operation: 'operation', drift: 'drifts', incident: 'incident', rpa_usage: 'rpa', rpa: 'rpa' }; + function Chip(props) { + return h('button', { type: 'button', className: 'tk-chip', title: props.hint, 'aria-label': props.hint, onClick: props.onClick }, + h(Icon, { name: props.icon, size: 14 }), + h('span', { className: 'tk-chip__text' }, props.children)); + } + function LinkButton(props) { + if (!props.link) { + return h('span', { className: 'tk-linkbtn tk-linkbtn--empty' }, + h(Icon, { name: props.icon, size: 14 }), + h('span', null, props.emptyLabel), + props.onAdd ? h(Button, { variant: 'ghost', size: 'sm', icon: 'plus', 'aria-label': props.addLabel, onClick: props.onAdd }) : null); + } + return h('a', { className: 'tk-linkbtn', href: props.link.href || '#', title: props.link.title, target: props.link.href ? '_blank' : undefined, rel: props.link.href ? 'noopener' : undefined }, + h(Icon, { name: props.icon, size: 14 }), + h('span', null, (props.prefix || '') + props.link.label), + h('span', { className: 'tk-linkbtn__ext' }, h(Icon, { name: 'links', size: 12 }))); + } + + function EventDrawer(props) { + var ev = props.event || {}; + var fields = ev.fields || []; + var history = ev.history || []; + var hasFacts = ev.service || ev.environment || ev.owner; + var hasLinks = ev.pullRequest !== undefined || ev.ticket !== undefined; + function filter(key, value) { return props.onFilter ? function () { props.onFilter(key, value); } : undefined; } + var stake = (ev.stakeholders || []).length ? 'Owner ' + ev.owner + ', stakeholders: ' + ev.stakeholders.join(', ') : 'Filter by owner ' + ev.owner; + return h('section', { className: 'tk-drawer', role: 'dialog', 'aria-modal': 'false', 'aria-label': ev.title || 'Event detail', style: props.style }, + h('header', { className: cx('tk-drawer__head', hasFacts && 'tk-drawer__head--band') }, + h('div', { className: 'tk-drawer__bar' }, + ev.type ? h('span', { className: 'tk-type' }, h(Icon, { name: TYPE_ICON[ev.type] || 'operation', size: 12 }), String(ev.type).replace('_', ' ')) : null, + h(StatusMark, { status: ev.status }), + h('span', { className: 'tk-meta' }, ev.time), + h(Button, { variant: 'ghost', size: 'sm', icon: 'close', 'aria-label': 'Close detail', onClick: props.onClose })), + h('h2', { className: 'tk-drawer__title' }, ev.title), + hasFacts ? h('div', { className: 'tk-chips' }, + ev.service ? h(Chip, { icon: 'service', hint: 'Filter by service ' + ev.service, onClick: filter('service', ev.service) }, ev.service) : null, + ev.environment ? h(Chip, { icon: 'environment', hint: 'Filter by environment ' + ev.environment, onClick: filter('env', ev.environment) }, ev.environment) : null, + ev.owner ? h(Chip, { icon: 'owner', hint: stake, onClick: filter('owner', ev.owner) }, ev.owner) : null, + ev.priority ? h('span', { className: 'tk-chip tk-chip--quiet', title: 'Priority' }, ev.priority) : null) + : (ev.meta ? h('span', { className: 'tk-meta' }, metaText(ev.meta)) : null), + hasLinks ? h('div', { className: 'tk-links' }, + h(LinkButton, { icon: 'pull-request', link: ev.pullRequest, prefix: 'PR ', emptyLabel: 'no pull request', addLabel: 'Link a pull request', onAdd: props.onLinkPullRequest }), + h(LinkButton, { icon: 'ticket', link: ev.ticket, emptyLabel: 'no ticket', addLabel: 'Link a ticket', onAdd: props.onLinkTicket })) : null), + ev.impact === true ? h('div', { className: 'tk-impact', role: 'note' }, + h(Icon, { name: 'impact', size: 16 }), + h('span', { className: 'tk-impact__title' }, 'Users impacted'), + ev.impactNote ? h('span', { className: 'tk-impact__note' }, ev.impactNote) : null) : null, + ev.impact === false ? h('div', { className: 'tk-impact tk-impact--none' }, + h(Icon, { name: 'impact', size: 14 }), + h('span', null, 'No user impact declared')) : null, + h('div', { className: 'tk-drawer__body' }, + ev.summary ? h('p', { style: { margin: 0, fontSize: '14px', lineHeight: '21px' } }, ev.summary) : null, + fields.length ? h('div', null, + h('h3', { className: 'tk-drawer__section-label' }, 'details'), + h('dl', { className: 'tk-fields' }, fields.map(function (f) { + return h(Fragment, { key: f[0] }, h('dt', null, f[0]), h('dd', null, f[2] ? h('a', { href: f[2] }, f[1]) : f[1])); + }))) : null, + history.length ? h('div', null, + h('h3', { className: 'tk-drawer__section-label' }, 'history'), + h('ol', { className: 'tk-history' }, history.map(function (it, i) { + return h('li', { key: i }, + h('time', null, it.time), + h('span', { className: 'tk-history__glyph tk-tone--' + (it.status || 'done') }, h(Glyph, { status: it.status || 'done', size: 12 })), + h('span', null, it.text)); + }))) : null), + props.actions === false ? null : h('footer', { className: 'tk-drawer__foot' }, + h(Button, { variant: 'ghost', icon: 'copy' }, 'Copy link'), + h(Button, { variant: 'secondary', icon: 'locks' }, 'Lock service'), + h(Button, { variant: 'primary' }, 'Edit event'))); + } + + var api = { + icons: icons, + Icon: Icon, + Logo: Logo, + Button: Button, + Kbd: Kbd, + Highlight: Highlight, + ImpactMark: ImpactMark, + StatusMark: StatusMark, + EventRow: EventRow, + EventList: EventList, + MetricStrip: MetricStrip, + SegmentedControl: SegmentedControl, + FilterBar: FilterBar, + Sidebar: Sidebar, + TimeRuler: TimeRuler, + CommandPalette: CommandPalette, + EventDrawer: EventDrawer + }; + window.Tracker = Object.assign(window.Tracker || {}, api); +})(); diff --git a/docs/design-system/reference/index.d.ts b/docs/design-system/reference/index.d.ts new file mode 100644 index 0000000..7e182ed --- /dev/null +++ b/docs/design-system/reference/index.d.ts @@ -0,0 +1,59 @@ +// Tracker — component props (documentation). Global: window.Tracker +import * as React from 'react'; + +export type Status = 'failed' | 'warning' | 'running' | 'done' | 'open'; +export type IconName = 'dashboard' | 'timeline' | 'streamline' | 'calendar' | 'overlaps' | 'insights' | 'catalog' | 'architecture' | 'compliance' | 'drifts' | 'rpa' | 'locks' | 'links' | 'docs' | 'deployment' | 'operation' | 'incident' | 'service' | 'now' | 'rows' | 'search' | 'filter' | 'plus' | 'close' | 'chevron-down' | 'arrow-right' | 'copy' | 'environment' | 'owner' | 'impact' | 'pull-request' | 'ticket'; + +export interface LogoProps { /** Symbol height in px. Default 24. */ size?: number; /** Show the "tracker" wordmark. Default true. */ word?: boolean; } +export interface IconProps { name: IconName; /** px, default 16 (drawn on a 16 px grid). */ size?: number; /** Accessible name; omit for decorative icons. */ title?: string; className?: string; } +export interface ButtonProps extends React.ButtonHTMLAttributes { variant?: 'primary' | 'secondary' | 'ghost'; size?: 'md' | 'sm'; icon?: IconName; iconRight?: IconName; } +export interface KbdProps { children: React.ReactNode; } +export interface HighlightProps { children: React.ReactNode; /** Mono 11 px chip for data ("running", "now 15:31"). */ data?: boolean; } +export interface StatusMarkProps { status: Status; /** Override the label text. */ text?: string; /** false renders the glyph alone, coloured by tone, with the label as its accessible name. Default true (tag). */ label?: boolean; /** Glyph size in px when label is false. Default 14. */ size?: number; } +export interface ImpactMarkProps { /** Icon only, 22 × 22. Use in rows and dense places. */ compact?: boolean; /** Label, default "impact". */ text?: string; /** Tooltip and accessible name, default "Users impacted". */ title?: string; } +export interface EventRowProps { id?: string | number; time: string; status: Status; title: string; /** Shows a compact ImpactMark before the status tag. */ impact?: boolean; meta?: string | string[]; density?: 'comfortable' | 'compact'; selected?: boolean; onSelect?: () => void; } +export interface EventListProps { events: EventRowProps[]; density?: 'comfortable' | 'compact'; selectedId?: string | number; onSelect?: (event: EventRowProps) => void; label?: string; } +export interface Metric { label: string; value: string; sub?: string; tone?: 'failed' | 'warning' | 'impact'; /** Comparison with the previous period, e.g. "−3.2 pts vs prev." */ delta?: string; /** worse: amber with a warning glyph; better and flat stay neutral. */ trend?: 'better' | 'worse' | 'flat'; } +export interface MetricStripProps { items: Metric[]; label?: string; } +export interface SegmentedControlProps { options: string[]; value?: string; defaultValue?: string; onChange?: (value: string) => void; label?: string; } +export interface Filter { key: string; value: string; } +export interface FilterBarProps { filters?: Filter[]; periods?: string[]; period?: string; scope?: string; onRemove?: (f: Filter, index: number) => void; onAdd?: () => void; onPeriodChange?: (p: string) => void; } +export interface NavItem { name: string; icon?: IconName; href?: string; count?: number | string; } +export interface SidebarProps { groups: { label: string; items: NavItem[] }[]; active?: string; logo?: boolean; homeHref?: string; /** Icon rail, 60 px wide: names become tooltips and accessible names, counts become badges. */ collapsed?: boolean; /** Shows a collapse / expand button at the bottom. */ onToggle?: () => void; } +export interface TimeRulerProps { /** First hour shown. Default 12. */ startHour?: number; /** Last hour. Default 18. */ endHour?: number; /** Alias of startHour (reserved on x-import: use start-hour). */ from?: number; /** Alias of endHour. */ to?: number; /** "HH:MM" */ now?: string; marks?: { at: string; status: Status }[]; width?: number; label?: string; } +export interface PaletteItem { label: string; icon?: IconName; hint?: string; kbd?: string; } +export interface CommandPaletteProps { groups: { label: string; items: PaletteItem[] }[]; defaultQuery?: string; placeholder?: string; autoFocus?: boolean; onSelect?: (item: PaletteItem) => void; onClose?: () => void; } +export type EventType = 'deployment' | 'operation' | 'drift' | 'incident' | 'rpa_usage'; +export interface EventLink { /** "#1287", "PAY-342" */ label: string; href?: string; /** PR or ticket title, shown as tooltip */ title?: string; } +export interface DrawerEvent { + status: Status; time: string; title: string; + type?: EventType; priority?: string; + /** Header chips; each one filters the time views on click (onFilter). */ + service?: string; environment?: string; owner?: string; stakeholders?: string[]; + /** true: full-width impact band in --impact; false: quiet "No user impact declared"; undefined: nothing. */ + impact?: boolean; impactNote?: string; + /** null shows a dashed empty slot with a + button; undefined hides the link row. */ + pullRequest?: EventLink | null; ticket?: EventLink | null; + /** Legacy meta line, shown only when service / environment / owner are absent. */ + meta?: string | string[]; + summary?: string; /** Secondary details: [label, value, href?] */ fields?: [string, string, string?][]; history?: { time: string; status: Status; text: string }[]; +} +export interface EventDrawerProps { event: DrawerEvent; onClose?: () => void; /** key is 'service' | 'env' | 'owner' */ onFilter?: (key: string, value: string) => void; onLinkTicket?: () => void; onLinkPullRequest?: () => void; actions?: boolean; style?: React.CSSProperties; } + +export declare const Logo: React.FC; +export declare const Icon: React.FC; +export declare const Button: React.FC; +export declare const Kbd: React.FC; +export declare const Highlight: React.FC; +export declare const ImpactMark: React.FC; +export declare const StatusMark: React.FC; +export declare const EventRow: React.FC; +export declare const EventList: React.FC; +export declare const MetricStrip: React.FC; +export declare const SegmentedControl: React.FC; +export declare const FilterBar: React.FC; +export declare const Sidebar: React.FC; +export declare const TimeRuler: React.FC; +export declare const CommandPalette: React.FC; +export declare const EventDrawer: React.FC; +export declare const icons: Record; diff --git a/docs/design-system/tokens.json b/docs/design-system/tokens.json new file mode 100644 index 0000000..20ad8e5 --- /dev/null +++ b/docs/design-system/tokens.json @@ -0,0 +1,138 @@ +{ + "name": "Tracker", + "version": 1, + "color": { + "themes": [ + { "id": "light", "name": "Clair" }, + { "id": "dark", "name": "Sombre" } + ], + "tokens": [ + { "name": "paper", "value": { "light": "#F3F1EC", "dark": "#131210" }, "usage": "Fond de page. Papier chaud le jour, carbone la nuit. Jamais de blanc pur ni de noir pur." }, + { "name": "surface", "value": { "light": "#FAF8F3", "dark": "#1B1A17" }, "usage": "Panneaux, cartes, barre latérale, barre du haut. Posé sur paper, délimité par un filet rule, sans ombre." }, + { "name": "sunken", "value": { "light": "#F1EEE7", "dark": "#22211D" }, "usage": "Creux : bande de métriques, piste du segmented control, survol d'une ligne ou d'un item de navigation." }, + { "name": "overlay", "value": { "light": "#FCFBF8", "dark": "#211F1C" }, "usage": "Surfaces flottantes uniquement : drawer, palette ⌘K, menus. Toujours avec shadow-overlay." }, + { "name": "scrim", "value": { "light": "rgba(20, 19, 17, 0.28)", "dark": "rgba(0, 0, 0, 0.55)" }, "usage": "Voile derrière la palette ⌘K. Le drawer n'en a pas : la liste reste lisible à côté." }, + + { "name": "rule", "value": { "light": "#DCD8CF", "dark": "#2E2C27" }, "usage": "Filet de 1 px : bords de panneaux, séparateurs de métriques. Décoratif, jamais seul pour délimiter un contrôle." }, + { "name": "rule-soft", "value": { "light": "#E7E3DA", "dark": "#262420" }, "usage": "Séparateurs entre lignes d'événements et entre lignes de tableau." }, + { "name": "rule-strong", "value": { "light": "#8C867A", "dark": "#6E695F" }, "usage": "Bord des contrôles (input, bouton secondary, filtre). 3.4:1 sur surface en clair, 3.2:1 en sombre." }, + { "name": "grid", "value": { "light": "#E9E5DC", "dark": "#25231F" }, "usage": "Trame millimétrée de la règle temporelle, au pas de 8 px. Ne jamais l'utiliser derrière du texte courant." }, + + { "name": "ink", "value": { "light": "#141311", "dark": "#EDEAE3" }, "usage": "Texte principal, titres, chiffres. 16:1 sur surface en clair, 14:1 en sombre." }, + { "name": "ink-hover", "value": { "light": "#2E2C28", "dark": "#D2CEC5" }, "usage": "Survol du bouton primary." }, + { "name": "on-ink", "value": { "light": "#FAF8F3", "dark": "#141311" }, "usage": "Texte et icônes posés sur un fond ink (bouton primary)." }, + { "name": "muted", "value": { "light": "#615C52", "dark": "#A39E93" }, "usage": "Métadonnées, heures, labels, statuts neutres (done, open). 6.3:1 sur surface en clair, 6.9:1 en sombre." }, + + { "name": "signature", "value": { "light": "#D4F53C", "dark": "#D4F53C" }, "usage": "Le surligneur. Marque le présent : maintenant, en cours, page active. Toujours en aplat sous un texte on-signature, jamais en couleur de texte sur paper." }, + { "name": "on-signature", "value": { "light": "#141311", "dark": "#141311" }, "usage": "Texte et icônes sur signature, dans les deux thèmes." }, + { "name": "signature-wash", "value": { "light": "#EEF5CC", "dark": "#2A2F16" }, "usage": "Ligne sélectionnée, item actif de la palette ⌘K. Le texte reste ink." }, + { "name": "needle", "value": { "light": "#141311", "dark": "#D4F53C" }, "usage": "Aiguille « now » de la règle temporelle et des graphiques. Encre le jour, chartreuse la nuit." }, + { "name": "focus-ring", "value": { "light": "#141311", "dark": "#D4F53C" }, "usage": "Anneau de focus clavier, 2 px, décalé de 2 px." }, + + { "name": "failed", "value": { "light": "#C2261C", "dark": "#FF7A6B" }, "usage": "Statut failed et seulement lui : échecs, incidents ouverts. 5.6:1 en clair, 6.8:1 en sombre. Toujours accompagné du glyphe carré marqué d'un ✕." }, + { "name": "failed-wash", "value": { "light": "#FBE4DF", "dark": "#3D1D18" }, "usage": "Fond de l'étiquette failed, et rien d'autre. Le texte failed y atteint 4.6:1 en clair, 6:1 en sombre." }, + { "name": "warning", "value": { "light": "#8F5A00", "dark": "#F0B44A" }, "usage": "Statut warning : dégradé, à surveiller, conflit potentiel. 5.6:1 en clair. Toujours accompagné du glyphe triangle marqué d'un !." }, + { "name": "warning-wash", "value": { "light": "#F7EBD3", "dark": "#382A12" }, "usage": "Fond de l'étiquette warning, et rien d'autre. Le texte warning y atteint 4.9:1 en clair, 7.5:1 en sombre." }, + { "name": "on-status", "value": { "light": "#FAF8F3", "dark": "#141311" }, "usage": "Le signe intérieur (✕, !) des glyphes pleins failed et warning." }, + { "name": "tag-line", "value": { "light": "#B5AFA3", "dark": "#55514A" }, "usage": "Filet des étiquettes neutres : plein pour done, pointillé pour open. Décoratif : le libellé muted porte l'information." }, + { "name": "impact", "value": { "light": "#B5126F", "dark": "#FF5CB0" }, "usage": "Impact utilisateur (attribut impact = true), et rien d'autre : bande du drawer, marqueur d'impact. Indépendant du statut : un déploiement réussi peut avoir un impact." }, + { "name": "on-impact", "value": { "light": "#FAF8F3", "dark": "#141311" }, "usage": "Texte et icônes posés sur impact. 6.1:1 en clair, 6.4:1 en sombre." }, + { "name": "impact-wash", "value": { "light": "#FBE3F0", "dark": "#3A1529" }, "usage": "Fond discret d'un marqueur d'impact dans une liste ou une cellule. Le texte y reste ink." }, + { "name": "chart-done", "value": { "light": "#8C867A", "dark": "#6E695F" }, "usage": "Segments « done » dans les graphiques. Neutre par principe : ce qui va bien ne réclame pas l'attention." }, + { "name": "chart-open", "value": { "light": "#D3CEC4", "dark": "#3A3732" }, "usage": "Segments « open » dans les graphiques : en attente, sans gravité." }, + + { "name": "type-deployment", "value": { "light": "#3355D6", "dark": "#7F9BFF" }, "usage": "Série « deployment » dans les graphiques par type d'événement. 5.8:1 sur surface en clair." }, + { "name": "type-operation", "value": { "light": "#0E8A80", "dark": "#3FC7BA" }, "usage": "Série « operation » dans les graphiques par type. 4:1 sur surface en clair." }, + { "name": "type-drift", "value": { "light": "#7A4CC9", "dark": "#B39BFF" }, "usage": "Série « drift » dans les graphiques par type. 5.2:1 sur surface en clair." }, + { "name": "type-rpa", "value": { "light": "#5F7186", "dark": "#9DB0C4" }, "usage": "Série « rpa usage » dans les graphiques par type. Les incidents restent en ink : ils se lisent par leur statut, pas par une couleur de série." }, + { "name": "data-1", "value": { "light": "#141311", "dark": "#EDEAE3" }, "usage": "Série catégorielle 1 (ex. production). Les catégories utilisent la rampe data, jamais les couleurs de statut." }, + { "name": "data-2", "value": { "light": "#5C574D", "dark": "#A39E93" }, "usage": "Série catégorielle 2 (ex. pré-production)." }, + { "name": "data-3", "value": { "light": "#9C968A", "dark": "#6E695F" }, "usage": "Série catégorielle 3 (ex. dev / integration)." }, + { "name": "data-4", "value": { "light": "#CFCAC0", "dark": "#45423B" }, "usage": "Série catégorielle 4 (ex. other). Au-delà de 4 séries, regrouper en « other »." } + ] + }, + "type": { + "fonts": [ + { "family": "Spline Sans", "file": "fonts/SplineSans.ttf", "weight": "300 700", "style": "normal" }, + { "family": "Spline Sans Mono", "file": "fonts/SplineSansMono.ttf", "weight": "300 700", "style": "normal" } + ], + "families": { + "sans": "\"Spline Sans\", \"Helvetica Neue\", Arial, sans-serif", + "mono": "\"Spline Sans Mono\", ui-monospace, \"SF Mono\", Menlo, Consolas, monospace" + }, + "groups": [ + { + "name": "Chiffres", + "family": "mono", + "styles": [ + { "name": "metric-xl", "fontSize": "36px", "lineHeight": "40px", "fontWeight": 500, "letterSpacing": "-0.04em", "sample": "236 events", "usage": "Chiffre héros d'une page (Insights). Un seul par écran." }, + { "name": "metric", "fontSize": "30px", "lineHeight": "34px", "fontWeight": 500, "letterSpacing": "-0.04em", "sample": "60.0%", "usage": "Valeurs de la bande de métriques. Chiffres tabulaires, jamais de zéros de remplissage (2, pas 02)." } + ] + }, + { + "name": "Titres", + "family": "sans", + "styles": [ + { "name": "title-lg", "fontSize": "28px", "lineHeight": "34px", "fontWeight": 600, "letterSpacing": "-0.035em", "sample": "Dashboard", "usage": "Titre de page, un par écran." }, + { "name": "title", "fontSize": "19px", "lineHeight": "26px", "fontWeight": 600, "letterSpacing": "-0.02em", "sample": "Deploy monitoring v2.5.5 → mco", "usage": "Titre du drawer, titres de sections longues." }, + { "name": "title-sm", "fontSize": "15px", "lineHeight": "22px", "fontWeight": 600, "letterSpacing": "-0.01em", "sample": "Event velocity", "usage": "En-tête de panneau ou de carte." } + ] + }, + { + "name": "Texte", + "family": "sans", + "styles": [ + { "name": "summary", "fontSize": "18px", "lineHeight": "28px", "fontWeight": 400, "letterSpacing": "-0.01em", "sample": "2 deployments failed and need attention.", "usage": "Phrase de synthèse en tête de page, lisible en trois secondes." }, + { "name": "body-strong", "fontSize": "14px", "lineHeight": "19px", "fontWeight": 500, "sample": "Terraform drift — firewall", "usage": "Titre d'une ligne d'événement, libellé d'item." }, + { "name": "body", "fontSize": "14px", "lineHeight": "21px", "fontWeight": 400, "sample": "Two deployments overlap on payment-service between 15:00 and 15:30.", "usage": "Texte courant, descriptions, contenu du drawer." }, + { "name": "caption", "fontSize": "12px", "lineHeight": "16px", "fontWeight": 400, "sample": "3 of 5 finished", "usage": "Précision sous une métrique, aide sous un champ." } + ] + }, + { + "name": "Données", + "family": "mono", + "styles": [ + { "name": "data", "fontSize": "12px", "lineHeight": "16px", "fontWeight": 400, "sample": "15:04:12", "usage": "Heures, durées, versions, identifiants, valeurs du drawer." }, + { "name": "data-sm", "fontSize": "11px", "lineHeight": "16px", "fontWeight": 400, "sample": "deployment · mco · P3 · infra-team", "usage": "Ligne de métadonnées, labels de métriques, étiquettes de groupes de navigation. En minuscules, séparées par « · »." }, + { "name": "data-strong", "fontSize": "11px", "lineHeight": "16px", "fontWeight": 500, "sample": "running", "usage": "Texte dans un surligneur, statut actif." } + ] + } + ] + }, + "spacing": { + "tokens": [ + { "name": "space-1", "value": "4px", "usage": "Écart entre glyphe et libellé serré, entre titre et métadonnées." }, + { "name": "space-2", "value": "8px", "usage": "Écart entre contrôles voisins, entre jetons de filtre." }, + { "name": "space-3", "value": "12px", "usage": "Gouttière des colonnes d'une ligne d'événement." }, + { "name": "space-4", "value": "16px", "usage": "Padding horizontal des métriques et des items de palette." }, + { "name": "space-5", "value": "20px", "usage": "Padding intérieur des panneaux." }, + { "name": "space-6", "value": "24px", "usage": "Écart entre panneaux, entre blocs d'une page." }, + { "name": "space-8", "value": "32px", "usage": "Marge de page." }, + { "name": "space-16", "value": "64px", "usage": "Grands dégagements (page vide, onboarding)." } + ] + }, + "radius": { + "tokens": [ + { "name": "radius-xs", "value": "3px", "usage": "Surligneur, touches clavier, segments de graphique." }, + { "name": "radius-sm", "value": "5px", "usage": "Items de navigation, segments du segmented control, jetons de filtre." }, + { "name": "radius-md", "value": "6px", "usage": "Boutons, champs, panneaux, cartes." }, + { "name": "radius-lg", "value": "8px", "usage": "Palette ⌘K, symbole du logo." } + ] + }, + "size": { + "tokens": [ + { "name": "control-md", "value": "38px", "usage": "Hauteur des boutons et champs par défaut (cible ≥ 38 px, 44 px sur tactile)." }, + { "name": "control-sm", "value": "30px", "usage": "Hauteur des contrôles secondaires et du segmented control." }, + { "name": "row-pad-comfy", "value": "12px", "usage": "Padding vertical d'une ligne en densité confort (≈ 62 px par ligne)." }, + { "name": "row-pad-compact", "value": "6px", "usage": "Padding vertical d'une ligne en densité compacte (≈ 32 px, une seule ligne de texte)." }, + { "name": "sidebar-w", "value": "232px", "usage": "Largeur de la barre latérale." }, + { "name": "drawer-w", "value": "480px", "usage": "Largeur du drawer de détail." } + ] + }, + "shadow": { + "note": "Filets, pas d'ombres. Une seule ombre existe, réservée à ce qui flotte au-dessus de la page.", + "tokens": [ + { "name": "shadow-overlay", "value": { "light": "0 1px 0 rgba(20, 19, 17, 0.04), 0 16px 40px rgba(20, 19, 17, 0.14)", "dark": "0 16px 40px rgba(0, 0, 0, 0.55)" }, "usage": "Drawer, palette ⌘K, menus déroulants. Jamais sur une carte ou un panneau posé." } + ] + } +} From 2cb9049eebfa75ac17c5c570bf7eae54c8169ad4 Mon Sep 17 00:00:00 2001 From: jplanckeel Date: Sat, 31 Oct 2026 18:24:54 +0100 Subject: [PATCH 2/8] feat(web): generate CSS variables from the design system tokens scripts/build-tokens.mjs turns src/design/tokens.json into src/design/tokens.css: light colours under :root, dark under .dark, an -rgb channel variable for each opaque colour, then fonts, type styles, spacing, radii, sizes and the overlay shadow. It runs before dev and every build script; a test fails when the generated file or the copy of tokens.json drifts. Co-Authored-By: Claude Opus 5.5 --- web/package.json | 6 + web/scripts/build-tokens.mjs | 109 ++++++++++++++++ web/src/design/tokens.css | 239 ++++++++++++++++++++++++++++++++++ web/src/design/tokens.json | 138 ++++++++++++++++++++ web/src/design/tokens.test.ts | 28 ++++ 5 files changed, 520 insertions(+) create mode 100644 web/scripts/build-tokens.mjs create mode 100644 web/src/design/tokens.css create mode 100644 web/src/design/tokens.json create mode 100644 web/src/design/tokens.test.ts diff --git a/web/package.json b/web/package.json index 1c81cbf..e1274e2 100644 --- a/web/package.json +++ b/web/package.json @@ -4,9 +4,15 @@ "private": true, "type": "module", "scripts": { + "tokens": "node scripts/build-tokens.mjs", + "tokens:check": "node scripts/build-tokens.mjs --check", + "predev": "npm run tokens", "dev": "vite", + "prebuild": "npm run tokens", "build": "vite build", + "prebuild:static": "npm run tokens", "build:static": "vite build --mode static", + "prebuild:check": "npm run tokens", "build:check": "tsc && vite build", "preview": "vite preview", "lint": "eslint . --report-unused-disable-directives --max-warnings 0", diff --git a/web/scripts/build-tokens.mjs b/web/scripts/build-tokens.mjs new file mode 100644 index 0000000..0c7c7c8 --- /dev/null +++ b/web/scripts/build-tokens.mjs @@ -0,0 +1,109 @@ +#!/usr/bin/env node +// Generates src/design/tokens.css from src/design/tokens.json. +// +// node scripts/build-tokens.mjs write the file +// node scripts/build-tokens.mjs --check exit 1 if the file is out of date +// +// Light colours go under :root, dark ones under .dark (the class set by +// ThemeContext). Every opaque colour also gets an "-rgb" channel variable +// (--paper-rgb: 243 241 236) for the rgb(var(--x) / α) form. Spacing, radii, +// sizes, font families and type styles go under :root. + +import { readFileSync, writeFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const SOURCE = resolve(root, 'src/design/tokens.json') +const TARGET = resolve(root, 'src/design/tokens.css') + +// Fontsource registers its variable fonts under " Variable". +// That name is put first, ahead of the family stack from tokens.json. +const FONTSOURCE_FAMILIES = { + sans: '"Spline Sans Variable"', + mono: '"Spline Sans Mono Variable"', +} + +function hexToChannels(value) { + const match = /^#([0-9a-f]{6})$/i.exec(value) + if (!match) return null + const n = parseInt(match[1], 16) + return `${(n >> 16) & 255} ${(n >> 8) & 255} ${n & 255}` +} + +function colorLines(tokens, theme) { + const lines = [] + for (const token of tokens) { + const value = token.value[theme] + lines.push(`--${token.name}: ${value};`) + const channels = hexToChannels(value) + if (channels) lines.push(`--${token.name}-rgb: ${channels};`) + } + return lines +} + +function typeLines(type) { + const lines = [] + for (const [name, stack] of Object.entries(type.families)) { + lines.push(`--font-${name}: ${FONTSOURCE_FAMILIES[name]}, ${stack};`) + } + for (const group of type.groups) { + for (const style of group.styles) { + const prefix = `--typo-${style.name}` + lines.push( + `${prefix}-family: var(--font-${group.family});`, + `${prefix}-size: ${style.fontSize};`, + `${prefix}-line-height: ${style.lineHeight};`, + `${prefix}-weight: ${style.fontWeight};`, + `${prefix}-tracking: ${style.letterSpacing ?? 'normal'};`, + ) + } + } + return lines +} + +function block(selector, lines) { + return `${selector} {\n${lines.map((l) => ` ${l}`).join('\n')}\n}\n` +} + +export function buildTokensCss(tokens) { + const flat = (family) => tokens[family].tokens.map((t) => `--${t.name}: ${t.value};`) + const shadows = (theme) => tokens.shadow.tokens.map((t) => `--${t.name}: ${t.value[theme]};`) + + return [ + '/*\n' + + ' * GENERATED FILE, DO NOT EDIT BY HAND.\n' + + ' * Source: src/design/tokens.json (copy of docs/design-system/tokens.json).\n' + + ' * Regenerate with: npm run tokens\n' + + ' */\n', + block(':root', [ + ...colorLines(tokens.color.tokens, 'light'), + ...shadows('light'), + ...typeLines(tokens.type), + ...flat('spacing'), + ...flat('radius'), + ...flat('size'), + ]), + block('.dark', [...colorLines(tokens.color.tokens, 'dark'), ...shadows('dark')]), + ].join('\n') +} + +function main() { + const css = buildTokensCss(JSON.parse(readFileSync(SOURCE, 'utf8'))) + if (process.argv.includes('--check')) { + let current = '' + try { + current = readFileSync(TARGET, 'utf8') + } catch { + // missing file: reported below as out of date + } + if (current !== css) { + console.error('src/design/tokens.css is out of date: run npm run tokens') + process.exit(1) + } + return + } + writeFileSync(TARGET, css) +} + +main() diff --git a/web/src/design/tokens.css b/web/src/design/tokens.css new file mode 100644 index 0000000..b5d8b13 --- /dev/null +++ b/web/src/design/tokens.css @@ -0,0 +1,239 @@ +/* + * GENERATED FILE, DO NOT EDIT BY HAND. + * Source: src/design/tokens.json (copy of docs/design-system/tokens.json). + * Regenerate with: npm run tokens + */ + +:root { + --paper: #F3F1EC; + --paper-rgb: 243 241 236; + --surface: #FAF8F3; + --surface-rgb: 250 248 243; + --sunken: #F1EEE7; + --sunken-rgb: 241 238 231; + --overlay: #FCFBF8; + --overlay-rgb: 252 251 248; + --scrim: rgba(20, 19, 17, 0.28); + --rule: #DCD8CF; + --rule-rgb: 220 216 207; + --rule-soft: #E7E3DA; + --rule-soft-rgb: 231 227 218; + --rule-strong: #8C867A; + --rule-strong-rgb: 140 134 122; + --grid: #E9E5DC; + --grid-rgb: 233 229 220; + --ink: #141311; + --ink-rgb: 20 19 17; + --ink-hover: #2E2C28; + --ink-hover-rgb: 46 44 40; + --on-ink: #FAF8F3; + --on-ink-rgb: 250 248 243; + --muted: #615C52; + --muted-rgb: 97 92 82; + --signature: #D4F53C; + --signature-rgb: 212 245 60; + --on-signature: #141311; + --on-signature-rgb: 20 19 17; + --signature-wash: #EEF5CC; + --signature-wash-rgb: 238 245 204; + --needle: #141311; + --needle-rgb: 20 19 17; + --focus-ring: #141311; + --focus-ring-rgb: 20 19 17; + --failed: #C2261C; + --failed-rgb: 194 38 28; + --failed-wash: #FBE4DF; + --failed-wash-rgb: 251 228 223; + --warning: #8F5A00; + --warning-rgb: 143 90 0; + --warning-wash: #F7EBD3; + --warning-wash-rgb: 247 235 211; + --on-status: #FAF8F3; + --on-status-rgb: 250 248 243; + --tag-line: #B5AFA3; + --tag-line-rgb: 181 175 163; + --impact: #B5126F; + --impact-rgb: 181 18 111; + --on-impact: #FAF8F3; + --on-impact-rgb: 250 248 243; + --impact-wash: #FBE3F0; + --impact-wash-rgb: 251 227 240; + --chart-done: #8C867A; + --chart-done-rgb: 140 134 122; + --chart-open: #D3CEC4; + --chart-open-rgb: 211 206 196; + --type-deployment: #3355D6; + --type-deployment-rgb: 51 85 214; + --type-operation: #0E8A80; + --type-operation-rgb: 14 138 128; + --type-drift: #7A4CC9; + --type-drift-rgb: 122 76 201; + --type-rpa: #5F7186; + --type-rpa-rgb: 95 113 134; + --data-1: #141311; + --data-1-rgb: 20 19 17; + --data-2: #5C574D; + --data-2-rgb: 92 87 77; + --data-3: #9C968A; + --data-3-rgb: 156 150 138; + --data-4: #CFCAC0; + --data-4-rgb: 207 202 192; + --shadow-overlay: 0 1px 0 rgba(20, 19, 17, 0.04), 0 16px 40px rgba(20, 19, 17, 0.14); + --font-sans: "Spline Sans Variable", "Spline Sans", "Helvetica Neue", Arial, sans-serif; + --font-mono: "Spline Sans Mono Variable", "Spline Sans Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace; + --typo-metric-xl-family: var(--font-mono); + --typo-metric-xl-size: 36px; + --typo-metric-xl-line-height: 40px; + --typo-metric-xl-weight: 500; + --typo-metric-xl-tracking: -0.04em; + --typo-metric-family: var(--font-mono); + --typo-metric-size: 30px; + --typo-metric-line-height: 34px; + --typo-metric-weight: 500; + --typo-metric-tracking: -0.04em; + --typo-title-lg-family: var(--font-sans); + --typo-title-lg-size: 28px; + --typo-title-lg-line-height: 34px; + --typo-title-lg-weight: 600; + --typo-title-lg-tracking: -0.035em; + --typo-title-family: var(--font-sans); + --typo-title-size: 19px; + --typo-title-line-height: 26px; + --typo-title-weight: 600; + --typo-title-tracking: -0.02em; + --typo-title-sm-family: var(--font-sans); + --typo-title-sm-size: 15px; + --typo-title-sm-line-height: 22px; + --typo-title-sm-weight: 600; + --typo-title-sm-tracking: -0.01em; + --typo-summary-family: var(--font-sans); + --typo-summary-size: 18px; + --typo-summary-line-height: 28px; + --typo-summary-weight: 400; + --typo-summary-tracking: -0.01em; + --typo-body-strong-family: var(--font-sans); + --typo-body-strong-size: 14px; + --typo-body-strong-line-height: 19px; + --typo-body-strong-weight: 500; + --typo-body-strong-tracking: normal; + --typo-body-family: var(--font-sans); + --typo-body-size: 14px; + --typo-body-line-height: 21px; + --typo-body-weight: 400; + --typo-body-tracking: normal; + --typo-caption-family: var(--font-sans); + --typo-caption-size: 12px; + --typo-caption-line-height: 16px; + --typo-caption-weight: 400; + --typo-caption-tracking: normal; + --typo-data-family: var(--font-mono); + --typo-data-size: 12px; + --typo-data-line-height: 16px; + --typo-data-weight: 400; + --typo-data-tracking: normal; + --typo-data-sm-family: var(--font-mono); + --typo-data-sm-size: 11px; + --typo-data-sm-line-height: 16px; + --typo-data-sm-weight: 400; + --typo-data-sm-tracking: normal; + --typo-data-strong-family: var(--font-mono); + --typo-data-strong-size: 11px; + --typo-data-strong-line-height: 16px; + --typo-data-strong-weight: 500; + --typo-data-strong-tracking: normal; + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; + --space-16: 64px; + --radius-xs: 3px; + --radius-sm: 5px; + --radius-md: 6px; + --radius-lg: 8px; + --control-md: 38px; + --control-sm: 30px; + --row-pad-comfy: 12px; + --row-pad-compact: 6px; + --sidebar-w: 232px; + --drawer-w: 480px; +} + +.dark { + --paper: #131210; + --paper-rgb: 19 18 16; + --surface: #1B1A17; + --surface-rgb: 27 26 23; + --sunken: #22211D; + --sunken-rgb: 34 33 29; + --overlay: #211F1C; + --overlay-rgb: 33 31 28; + --scrim: rgba(0, 0, 0, 0.55); + --rule: #2E2C27; + --rule-rgb: 46 44 39; + --rule-soft: #262420; + --rule-soft-rgb: 38 36 32; + --rule-strong: #6E695F; + --rule-strong-rgb: 110 105 95; + --grid: #25231F; + --grid-rgb: 37 35 31; + --ink: #EDEAE3; + --ink-rgb: 237 234 227; + --ink-hover: #D2CEC5; + --ink-hover-rgb: 210 206 197; + --on-ink: #141311; + --on-ink-rgb: 20 19 17; + --muted: #A39E93; + --muted-rgb: 163 158 147; + --signature: #D4F53C; + --signature-rgb: 212 245 60; + --on-signature: #141311; + --on-signature-rgb: 20 19 17; + --signature-wash: #2A2F16; + --signature-wash-rgb: 42 47 22; + --needle: #D4F53C; + --needle-rgb: 212 245 60; + --focus-ring: #D4F53C; + --focus-ring-rgb: 212 245 60; + --failed: #FF7A6B; + --failed-rgb: 255 122 107; + --failed-wash: #3D1D18; + --failed-wash-rgb: 61 29 24; + --warning: #F0B44A; + --warning-rgb: 240 180 74; + --warning-wash: #382A12; + --warning-wash-rgb: 56 42 18; + --on-status: #141311; + --on-status-rgb: 20 19 17; + --tag-line: #55514A; + --tag-line-rgb: 85 81 74; + --impact: #FF5CB0; + --impact-rgb: 255 92 176; + --on-impact: #141311; + --on-impact-rgb: 20 19 17; + --impact-wash: #3A1529; + --impact-wash-rgb: 58 21 41; + --chart-done: #6E695F; + --chart-done-rgb: 110 105 95; + --chart-open: #3A3732; + --chart-open-rgb: 58 55 50; + --type-deployment: #7F9BFF; + --type-deployment-rgb: 127 155 255; + --type-operation: #3FC7BA; + --type-operation-rgb: 63 199 186; + --type-drift: #B39BFF; + --type-drift-rgb: 179 155 255; + --type-rpa: #9DB0C4; + --type-rpa-rgb: 157 176 196; + --data-1: #EDEAE3; + --data-1-rgb: 237 234 227; + --data-2: #A39E93; + --data-2-rgb: 163 158 147; + --data-3: #6E695F; + --data-3-rgb: 110 105 95; + --data-4: #45423B; + --data-4-rgb: 69 66 59; + --shadow-overlay: 0 16px 40px rgba(0, 0, 0, 0.55); +} diff --git a/web/src/design/tokens.json b/web/src/design/tokens.json new file mode 100644 index 0000000..20ad8e5 --- /dev/null +++ b/web/src/design/tokens.json @@ -0,0 +1,138 @@ +{ + "name": "Tracker", + "version": 1, + "color": { + "themes": [ + { "id": "light", "name": "Clair" }, + { "id": "dark", "name": "Sombre" } + ], + "tokens": [ + { "name": "paper", "value": { "light": "#F3F1EC", "dark": "#131210" }, "usage": "Fond de page. Papier chaud le jour, carbone la nuit. Jamais de blanc pur ni de noir pur." }, + { "name": "surface", "value": { "light": "#FAF8F3", "dark": "#1B1A17" }, "usage": "Panneaux, cartes, barre latérale, barre du haut. Posé sur paper, délimité par un filet rule, sans ombre." }, + { "name": "sunken", "value": { "light": "#F1EEE7", "dark": "#22211D" }, "usage": "Creux : bande de métriques, piste du segmented control, survol d'une ligne ou d'un item de navigation." }, + { "name": "overlay", "value": { "light": "#FCFBF8", "dark": "#211F1C" }, "usage": "Surfaces flottantes uniquement : drawer, palette ⌘K, menus. Toujours avec shadow-overlay." }, + { "name": "scrim", "value": { "light": "rgba(20, 19, 17, 0.28)", "dark": "rgba(0, 0, 0, 0.55)" }, "usage": "Voile derrière la palette ⌘K. Le drawer n'en a pas : la liste reste lisible à côté." }, + + { "name": "rule", "value": { "light": "#DCD8CF", "dark": "#2E2C27" }, "usage": "Filet de 1 px : bords de panneaux, séparateurs de métriques. Décoratif, jamais seul pour délimiter un contrôle." }, + { "name": "rule-soft", "value": { "light": "#E7E3DA", "dark": "#262420" }, "usage": "Séparateurs entre lignes d'événements et entre lignes de tableau." }, + { "name": "rule-strong", "value": { "light": "#8C867A", "dark": "#6E695F" }, "usage": "Bord des contrôles (input, bouton secondary, filtre). 3.4:1 sur surface en clair, 3.2:1 en sombre." }, + { "name": "grid", "value": { "light": "#E9E5DC", "dark": "#25231F" }, "usage": "Trame millimétrée de la règle temporelle, au pas de 8 px. Ne jamais l'utiliser derrière du texte courant." }, + + { "name": "ink", "value": { "light": "#141311", "dark": "#EDEAE3" }, "usage": "Texte principal, titres, chiffres. 16:1 sur surface en clair, 14:1 en sombre." }, + { "name": "ink-hover", "value": { "light": "#2E2C28", "dark": "#D2CEC5" }, "usage": "Survol du bouton primary." }, + { "name": "on-ink", "value": { "light": "#FAF8F3", "dark": "#141311" }, "usage": "Texte et icônes posés sur un fond ink (bouton primary)." }, + { "name": "muted", "value": { "light": "#615C52", "dark": "#A39E93" }, "usage": "Métadonnées, heures, labels, statuts neutres (done, open). 6.3:1 sur surface en clair, 6.9:1 en sombre." }, + + { "name": "signature", "value": { "light": "#D4F53C", "dark": "#D4F53C" }, "usage": "Le surligneur. Marque le présent : maintenant, en cours, page active. Toujours en aplat sous un texte on-signature, jamais en couleur de texte sur paper." }, + { "name": "on-signature", "value": { "light": "#141311", "dark": "#141311" }, "usage": "Texte et icônes sur signature, dans les deux thèmes." }, + { "name": "signature-wash", "value": { "light": "#EEF5CC", "dark": "#2A2F16" }, "usage": "Ligne sélectionnée, item actif de la palette ⌘K. Le texte reste ink." }, + { "name": "needle", "value": { "light": "#141311", "dark": "#D4F53C" }, "usage": "Aiguille « now » de la règle temporelle et des graphiques. Encre le jour, chartreuse la nuit." }, + { "name": "focus-ring", "value": { "light": "#141311", "dark": "#D4F53C" }, "usage": "Anneau de focus clavier, 2 px, décalé de 2 px." }, + + { "name": "failed", "value": { "light": "#C2261C", "dark": "#FF7A6B" }, "usage": "Statut failed et seulement lui : échecs, incidents ouverts. 5.6:1 en clair, 6.8:1 en sombre. Toujours accompagné du glyphe carré marqué d'un ✕." }, + { "name": "failed-wash", "value": { "light": "#FBE4DF", "dark": "#3D1D18" }, "usage": "Fond de l'étiquette failed, et rien d'autre. Le texte failed y atteint 4.6:1 en clair, 6:1 en sombre." }, + { "name": "warning", "value": { "light": "#8F5A00", "dark": "#F0B44A" }, "usage": "Statut warning : dégradé, à surveiller, conflit potentiel. 5.6:1 en clair. Toujours accompagné du glyphe triangle marqué d'un !." }, + { "name": "warning-wash", "value": { "light": "#F7EBD3", "dark": "#382A12" }, "usage": "Fond de l'étiquette warning, et rien d'autre. Le texte warning y atteint 4.9:1 en clair, 7.5:1 en sombre." }, + { "name": "on-status", "value": { "light": "#FAF8F3", "dark": "#141311" }, "usage": "Le signe intérieur (✕, !) des glyphes pleins failed et warning." }, + { "name": "tag-line", "value": { "light": "#B5AFA3", "dark": "#55514A" }, "usage": "Filet des étiquettes neutres : plein pour done, pointillé pour open. Décoratif : le libellé muted porte l'information." }, + { "name": "impact", "value": { "light": "#B5126F", "dark": "#FF5CB0" }, "usage": "Impact utilisateur (attribut impact = true), et rien d'autre : bande du drawer, marqueur d'impact. Indépendant du statut : un déploiement réussi peut avoir un impact." }, + { "name": "on-impact", "value": { "light": "#FAF8F3", "dark": "#141311" }, "usage": "Texte et icônes posés sur impact. 6.1:1 en clair, 6.4:1 en sombre." }, + { "name": "impact-wash", "value": { "light": "#FBE3F0", "dark": "#3A1529" }, "usage": "Fond discret d'un marqueur d'impact dans une liste ou une cellule. Le texte y reste ink." }, + { "name": "chart-done", "value": { "light": "#8C867A", "dark": "#6E695F" }, "usage": "Segments « done » dans les graphiques. Neutre par principe : ce qui va bien ne réclame pas l'attention." }, + { "name": "chart-open", "value": { "light": "#D3CEC4", "dark": "#3A3732" }, "usage": "Segments « open » dans les graphiques : en attente, sans gravité." }, + + { "name": "type-deployment", "value": { "light": "#3355D6", "dark": "#7F9BFF" }, "usage": "Série « deployment » dans les graphiques par type d'événement. 5.8:1 sur surface en clair." }, + { "name": "type-operation", "value": { "light": "#0E8A80", "dark": "#3FC7BA" }, "usage": "Série « operation » dans les graphiques par type. 4:1 sur surface en clair." }, + { "name": "type-drift", "value": { "light": "#7A4CC9", "dark": "#B39BFF" }, "usage": "Série « drift » dans les graphiques par type. 5.2:1 sur surface en clair." }, + { "name": "type-rpa", "value": { "light": "#5F7186", "dark": "#9DB0C4" }, "usage": "Série « rpa usage » dans les graphiques par type. Les incidents restent en ink : ils se lisent par leur statut, pas par une couleur de série." }, + { "name": "data-1", "value": { "light": "#141311", "dark": "#EDEAE3" }, "usage": "Série catégorielle 1 (ex. production). Les catégories utilisent la rampe data, jamais les couleurs de statut." }, + { "name": "data-2", "value": { "light": "#5C574D", "dark": "#A39E93" }, "usage": "Série catégorielle 2 (ex. pré-production)." }, + { "name": "data-3", "value": { "light": "#9C968A", "dark": "#6E695F" }, "usage": "Série catégorielle 3 (ex. dev / integration)." }, + { "name": "data-4", "value": { "light": "#CFCAC0", "dark": "#45423B" }, "usage": "Série catégorielle 4 (ex. other). Au-delà de 4 séries, regrouper en « other »." } + ] + }, + "type": { + "fonts": [ + { "family": "Spline Sans", "file": "fonts/SplineSans.ttf", "weight": "300 700", "style": "normal" }, + { "family": "Spline Sans Mono", "file": "fonts/SplineSansMono.ttf", "weight": "300 700", "style": "normal" } + ], + "families": { + "sans": "\"Spline Sans\", \"Helvetica Neue\", Arial, sans-serif", + "mono": "\"Spline Sans Mono\", ui-monospace, \"SF Mono\", Menlo, Consolas, monospace" + }, + "groups": [ + { + "name": "Chiffres", + "family": "mono", + "styles": [ + { "name": "metric-xl", "fontSize": "36px", "lineHeight": "40px", "fontWeight": 500, "letterSpacing": "-0.04em", "sample": "236 events", "usage": "Chiffre héros d'une page (Insights). Un seul par écran." }, + { "name": "metric", "fontSize": "30px", "lineHeight": "34px", "fontWeight": 500, "letterSpacing": "-0.04em", "sample": "60.0%", "usage": "Valeurs de la bande de métriques. Chiffres tabulaires, jamais de zéros de remplissage (2, pas 02)." } + ] + }, + { + "name": "Titres", + "family": "sans", + "styles": [ + { "name": "title-lg", "fontSize": "28px", "lineHeight": "34px", "fontWeight": 600, "letterSpacing": "-0.035em", "sample": "Dashboard", "usage": "Titre de page, un par écran." }, + { "name": "title", "fontSize": "19px", "lineHeight": "26px", "fontWeight": 600, "letterSpacing": "-0.02em", "sample": "Deploy monitoring v2.5.5 → mco", "usage": "Titre du drawer, titres de sections longues." }, + { "name": "title-sm", "fontSize": "15px", "lineHeight": "22px", "fontWeight": 600, "letterSpacing": "-0.01em", "sample": "Event velocity", "usage": "En-tête de panneau ou de carte." } + ] + }, + { + "name": "Texte", + "family": "sans", + "styles": [ + { "name": "summary", "fontSize": "18px", "lineHeight": "28px", "fontWeight": 400, "letterSpacing": "-0.01em", "sample": "2 deployments failed and need attention.", "usage": "Phrase de synthèse en tête de page, lisible en trois secondes." }, + { "name": "body-strong", "fontSize": "14px", "lineHeight": "19px", "fontWeight": 500, "sample": "Terraform drift — firewall", "usage": "Titre d'une ligne d'événement, libellé d'item." }, + { "name": "body", "fontSize": "14px", "lineHeight": "21px", "fontWeight": 400, "sample": "Two deployments overlap on payment-service between 15:00 and 15:30.", "usage": "Texte courant, descriptions, contenu du drawer." }, + { "name": "caption", "fontSize": "12px", "lineHeight": "16px", "fontWeight": 400, "sample": "3 of 5 finished", "usage": "Précision sous une métrique, aide sous un champ." } + ] + }, + { + "name": "Données", + "family": "mono", + "styles": [ + { "name": "data", "fontSize": "12px", "lineHeight": "16px", "fontWeight": 400, "sample": "15:04:12", "usage": "Heures, durées, versions, identifiants, valeurs du drawer." }, + { "name": "data-sm", "fontSize": "11px", "lineHeight": "16px", "fontWeight": 400, "sample": "deployment · mco · P3 · infra-team", "usage": "Ligne de métadonnées, labels de métriques, étiquettes de groupes de navigation. En minuscules, séparées par « · »." }, + { "name": "data-strong", "fontSize": "11px", "lineHeight": "16px", "fontWeight": 500, "sample": "running", "usage": "Texte dans un surligneur, statut actif." } + ] + } + ] + }, + "spacing": { + "tokens": [ + { "name": "space-1", "value": "4px", "usage": "Écart entre glyphe et libellé serré, entre titre et métadonnées." }, + { "name": "space-2", "value": "8px", "usage": "Écart entre contrôles voisins, entre jetons de filtre." }, + { "name": "space-3", "value": "12px", "usage": "Gouttière des colonnes d'une ligne d'événement." }, + { "name": "space-4", "value": "16px", "usage": "Padding horizontal des métriques et des items de palette." }, + { "name": "space-5", "value": "20px", "usage": "Padding intérieur des panneaux." }, + { "name": "space-6", "value": "24px", "usage": "Écart entre panneaux, entre blocs d'une page." }, + { "name": "space-8", "value": "32px", "usage": "Marge de page." }, + { "name": "space-16", "value": "64px", "usage": "Grands dégagements (page vide, onboarding)." } + ] + }, + "radius": { + "tokens": [ + { "name": "radius-xs", "value": "3px", "usage": "Surligneur, touches clavier, segments de graphique." }, + { "name": "radius-sm", "value": "5px", "usage": "Items de navigation, segments du segmented control, jetons de filtre." }, + { "name": "radius-md", "value": "6px", "usage": "Boutons, champs, panneaux, cartes." }, + { "name": "radius-lg", "value": "8px", "usage": "Palette ⌘K, symbole du logo." } + ] + }, + "size": { + "tokens": [ + { "name": "control-md", "value": "38px", "usage": "Hauteur des boutons et champs par défaut (cible ≥ 38 px, 44 px sur tactile)." }, + { "name": "control-sm", "value": "30px", "usage": "Hauteur des contrôles secondaires et du segmented control." }, + { "name": "row-pad-comfy", "value": "12px", "usage": "Padding vertical d'une ligne en densité confort (≈ 62 px par ligne)." }, + { "name": "row-pad-compact", "value": "6px", "usage": "Padding vertical d'une ligne en densité compacte (≈ 32 px, une seule ligne de texte)." }, + { "name": "sidebar-w", "value": "232px", "usage": "Largeur de la barre latérale." }, + { "name": "drawer-w", "value": "480px", "usage": "Largeur du drawer de détail." } + ] + }, + "shadow": { + "note": "Filets, pas d'ombres. Une seule ombre existe, réservée à ce qui flotte au-dessus de la page.", + "tokens": [ + { "name": "shadow-overlay", "value": { "light": "0 1px 0 rgba(20, 19, 17, 0.04), 0 16px 40px rgba(20, 19, 17, 0.14)", "dark": "0 16px 40px rgba(0, 0, 0, 0.55)" }, "usage": "Drawer, palette ⌘K, menus déroulants. Jamais sur une carte ou un panneau posé." } + ] + } +} diff --git a/web/src/design/tokens.test.ts b/web/src/design/tokens.test.ts new file mode 100644 index 0000000..6c256be --- /dev/null +++ b/web/src/design/tokens.test.ts @@ -0,0 +1,28 @@ +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { describe, expect, it } from 'vitest' +import tokens from './tokens.json' + +const web = resolve(__dirname, '../..') + +describe('design tokens', () => { + it('keeps tokens.json in sync with docs/design-system', () => { + const source = readFileSync(resolve(web, '../docs/design-system/tokens.json'), 'utf8') + expect(JSON.parse(source)).toEqual(tokens) + }) + + it('keeps the generated tokens.css up to date', () => { + // Throws (non-zero exit) when tokens.css differs from what tokens.json produces. + expect(() => + execFileSync(process.execPath, ['scripts/build-tokens.mjs', '--check'], { cwd: web, stdio: 'pipe' }), + ).not.toThrow() + }) + + it('gives every colour a light and a dark value', () => { + for (const token of tokens.color.tokens) { + expect(token.value.light, token.name).toBeTruthy() + expect(token.value.dark, token.name).toBeTruthy() + } + }) +}) From 162a63598840bd9e65f76edc8c257743968a4813 Mon Sep 17 00:00:00 2001 From: jplanckeel Date: Sat, 31 Oct 2026 18:26:54 +0100 Subject: [PATCH 3/8] feat(web): expose the design tokens as Tailwind utilities index.css imports tokens.css and maps every colour token in an @theme inline block (bg-paper, text-ink, border-rule-strong, bg-signature, text-impact...), plus spacing and size aliases, shadow-overlay and the twelve type styles (text-metric, text-title, text-summary, text-data-sm...). Fonts and radii share Tailwind's theme names, so the tokens override its defaults directly. cn() now knows the type styles: tailwind-merge used to read text-title as a colour and drop it next to text-ink. Co-Authored-By: Claude Opus 5.5 --- web/src/design/tokens.test.ts | 14 ++- web/src/index.css | 160 ++++++++++++++++++++++++++++++++++ web/src/lib/utils.test.ts | 25 ++++++ web/src/lib/utils.ts | 17 +++- 4 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 web/src/lib/utils.test.ts diff --git a/web/src/design/tokens.test.ts b/web/src/design/tokens.test.ts index 6c256be..b5bc4cd 100644 --- a/web/src/design/tokens.test.ts +++ b/web/src/design/tokens.test.ts @@ -1,10 +1,12 @@ import { execFileSync } from 'node:child_process' import { readFileSync } from 'node:fs' import { resolve } from 'node:path' +import process from 'node:process' import { describe, expect, it } from 'vitest' import tokens from './tokens.json' -const web = resolve(__dirname, '../..') +// Vitest runs from web/. +const web = process.cwd() describe('design tokens', () => { it('keeps tokens.json in sync with docs/design-system', () => { @@ -19,6 +21,16 @@ describe('design tokens', () => { ).not.toThrow() }) + it('exposes every colour and type style to Tailwind in index.css', () => { + const css = readFileSync(resolve(web, 'src/index.css'), 'utf8') + for (const token of tokens.color.tokens) { + expect(css, token.name).toContain(`--color-${token.name}: var(--${token.name});`) + } + for (const style of tokens.type.groups.flatMap((g) => g.styles)) { + expect(css, style.name).toContain(`@utility text-${style.name} {`) + } + }) + it('gives every colour a light and a dark value', () => { for (const token of tokens.color.tokens) { expect(token.value.light, token.name).toBeTruthy() diff --git a/web/src/index.css b/web/src/index.css index 9db0975..6963fef 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -1,8 +1,168 @@ @import 'tailwindcss'; +/* Design system tokens (generated from src/design/tokens.json). Unlayered on purpose: + --font-sans, --font-mono and --radius-xs..lg share Tailwind's theme names, and these + values override Tailwind's defaults, so font-sans, font-mono and rounded-* follow the tokens. */ +@import './design/tokens.css'; @import '@fortawesome/fontawesome-svg-core/styles.css' layer(base); @custom-variant dark (&:is(.dark *)); +/* Design system: tokens exposed as utilities (bg-paper, text-ink, border-rule-strong, + bg-signature, text-impact…). Colours only appear in components through these. */ +@theme inline { + --color-paper: var(--paper); + --color-surface: var(--surface); + --color-sunken: var(--sunken); + --color-overlay: var(--overlay); + --color-scrim: var(--scrim); + --color-rule: var(--rule); + --color-rule-soft: var(--rule-soft); + --color-rule-strong: var(--rule-strong); + --color-grid: var(--grid); + --color-ink: var(--ink); + --color-ink-hover: var(--ink-hover); + --color-on-ink: var(--on-ink); + --color-muted: var(--muted); + --color-signature: var(--signature); + --color-on-signature: var(--on-signature); + --color-signature-wash: var(--signature-wash); + --color-needle: var(--needle); + --color-focus-ring: var(--focus-ring); + --color-failed: var(--failed); + --color-failed-wash: var(--failed-wash); + --color-warning: var(--warning); + --color-warning-wash: var(--warning-wash); + --color-on-status: var(--on-status); + --color-tag-line: var(--tag-line); + --color-impact: var(--impact); + --color-on-impact: var(--on-impact); + --color-impact-wash: var(--impact-wash); + --color-chart-done: var(--chart-done); + --color-chart-open: var(--chart-open); + --color-type-deployment: var(--type-deployment); + --color-type-operation: var(--type-operation); + --color-type-drift: var(--type-drift); + --color-type-rpa: var(--type-rpa); + --color-data-1: var(--data-1); + --color-data-2: var(--data-2); + --color-data-3: var(--data-3); + --color-data-4: var(--data-4); + + --font-grotesk: var(--font-sans); + + --spacing-space-1: var(--space-1); + --spacing-space-2: var(--space-2); + --spacing-space-3: var(--space-3); + --spacing-space-4: var(--space-4); + --spacing-space-5: var(--space-5); + --spacing-space-6: var(--space-6); + --spacing-space-8: var(--space-8); + --spacing-space-16: var(--space-16); + --spacing-control-md: var(--control-md); + --spacing-control-sm: var(--control-sm); + --spacing-row-pad-comfy: var(--row-pad-comfy); + --spacing-row-pad-compact: var(--row-pad-compact); + --spacing-sidebar-w: var(--sidebar-w); + --spacing-drawer-w: var(--drawer-w); +} + +@utility shadow-overlay { + box-shadow: var(--shadow-overlay); +} + +/* Type styles from tokens.json: family, size, line height, weight and tracking. + The mono styles carry tabular figures. */ +@utility text-metric-xl { + font-family: var(--typo-metric-xl-family); + font-size: var(--typo-metric-xl-size); + line-height: var(--typo-metric-xl-line-height); + font-weight: var(--typo-metric-xl-weight); + letter-spacing: var(--typo-metric-xl-tracking); + font-variant-numeric: tabular-nums; +} +@utility text-metric { + font-family: var(--typo-metric-family); + font-size: var(--typo-metric-size); + line-height: var(--typo-metric-line-height); + font-weight: var(--typo-metric-weight); + letter-spacing: var(--typo-metric-tracking); + font-variant-numeric: tabular-nums; +} +@utility text-title-lg { + font-family: var(--typo-title-lg-family); + font-size: var(--typo-title-lg-size); + line-height: var(--typo-title-lg-line-height); + font-weight: var(--typo-title-lg-weight); + letter-spacing: var(--typo-title-lg-tracking); +} +@utility text-title { + font-family: var(--typo-title-family); + font-size: var(--typo-title-size); + line-height: var(--typo-title-line-height); + font-weight: var(--typo-title-weight); + letter-spacing: var(--typo-title-tracking); +} +@utility text-title-sm { + font-family: var(--typo-title-sm-family); + font-size: var(--typo-title-sm-size); + line-height: var(--typo-title-sm-line-height); + font-weight: var(--typo-title-sm-weight); + letter-spacing: var(--typo-title-sm-tracking); +} +@utility text-summary { + font-family: var(--typo-summary-family); + font-size: var(--typo-summary-size); + line-height: var(--typo-summary-line-height); + font-weight: var(--typo-summary-weight); + letter-spacing: var(--typo-summary-tracking); +} +@utility text-body-strong { + font-family: var(--typo-body-strong-family); + font-size: var(--typo-body-strong-size); + line-height: var(--typo-body-strong-line-height); + font-weight: var(--typo-body-strong-weight); + letter-spacing: var(--typo-body-strong-tracking); +} +@utility text-body { + font-family: var(--typo-body-family); + font-size: var(--typo-body-size); + line-height: var(--typo-body-line-height); + font-weight: var(--typo-body-weight); + letter-spacing: var(--typo-body-tracking); +} +@utility text-caption { + font-family: var(--typo-caption-family); + font-size: var(--typo-caption-size); + line-height: var(--typo-caption-line-height); + font-weight: var(--typo-caption-weight); + letter-spacing: var(--typo-caption-tracking); +} +@utility text-data { + font-family: var(--typo-data-family); + font-size: var(--typo-data-size); + line-height: var(--typo-data-line-height); + font-weight: var(--typo-data-weight); + letter-spacing: var(--typo-data-tracking); + font-variant-numeric: tabular-nums; +} +@utility text-data-sm { + font-family: var(--typo-data-sm-family); + font-size: var(--typo-data-sm-size); + line-height: var(--typo-data-sm-line-height); + font-weight: var(--typo-data-sm-weight); + letter-spacing: var(--typo-data-sm-tracking); + font-variant-numeric: tabular-nums; +} +@utility text-data-strong { + font-family: var(--typo-data-strong-family); + font-size: var(--typo-data-strong-size); + line-height: var(--typo-data-strong-line-height); + font-weight: var(--typo-data-strong-weight); + letter-spacing: var(--typo-data-strong-tracking); + font-variant-numeric: tabular-nums; +} + +/* Legacy theme (Intergalactic), removed in lot 5. */ @theme { /* Tailwind 3 line heights. v4 expresses them as unitless ratios, which a smaller child (text-[10px] inside text-xs) then inherits and shrinks by. */ diff --git a/web/src/lib/utils.test.ts b/web/src/lib/utils.test.ts new file mode 100644 index 0000000..e9ccc67 --- /dev/null +++ b/web/src/lib/utils.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from 'vitest' +import { cn, typeStyles } from './utils' + +describe('cn', () => { + it('keeps a type style next to a text colour', () => { + expect(cn('text-title', 'text-ink')).toBe('text-title text-ink') + expect(cn('text-muted', 'text-data-sm')).toBe('text-muted text-data-sm') + }) + + it('lets a later type style replace an earlier one', () => { + expect(cn('text-body', 'text-caption')).toBe('text-caption') + expect(cn('text-sm', 'text-data')).toBe('text-data') + }) + + it('treats shadow-overlay as a shadow', () => { + expect(cn('shadow-xs', 'shadow-overlay')).toBe('shadow-overlay') + }) + + it('knows the twelve type styles of tokens.json', () => { + expect(typeStyles).toEqual([ + 'metric-xl', 'metric', 'title-lg', 'title', 'title-sm', 'summary', + 'body-strong', 'body', 'caption', 'data', 'data-sm', 'data-strong', + ]) + }) +}) diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index d084cca..043adbb 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -1,5 +1,20 @@ import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" +import { extendTailwindMerge } from "tailwind-merge" +import tokens from "@/design/tokens.json" + +/** Type styles of the design system, exposed as `text-` utilities in index.css. */ +export const typeStyles = tokens.type.groups.flatMap((group) => group.styles.map((style) => style.name)) + +// Without this, tailwind-merge reads `text-title` as a text colour and drops it +// in favour of `text-ink` (or the reverse). +const twMerge = extendTailwindMerge({ + extend: { + classGroups: { + "font-size": [{ text: typeStyles }], + shadow: [{ shadow: ["overlay"] }], + }, + }, +}) export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) From f2bdf9f65accffb7cd39792636ec89799f6ed820 Mon Sep 17 00:00:00 2001 From: jplanckeel Date: Sat, 31 Oct 2026 18:31:10 +0100 Subject: [PATCH 4/8] feat(web): switch to Spline Sans and Spline Sans Mono Load both variable fonts from Fontsource and drop the Google Fonts link (Plus Jakarta Sans, IBM Plex Mono). The body uses --font-sans. Inline 'Space Grotesk' declarations are removed so titles inherit Spline Sans; inline 'JetBrains Mono' ones now point to --font-mono. Co-Authored-By: Claude Opus 5.5 --- web/index.html | 4 ---- web/package-lock.json | 20 ++++++++++++++++++++ web/package.json | 2 ++ web/src/components/EventDetailsModal.tsx | 4 ++-- web/src/components/FormPanel.tsx | 2 +- web/src/index.css | 6 +----- web/src/main.tsx | 2 ++ web/src/pages/CatalogDependencies.tsx | 10 +++++----- web/src/pages/CatalogDetail.tsx | 2 +- web/src/pages/CatalogTable.tsx | 4 ++-- web/src/pages/CreateCatalog.tsx | 9 ++++----- web/src/pages/CreateDrift.tsx | 7 +++---- web/src/pages/CreateEvent.tsx | 9 ++++----- web/src/pages/CreateLock.tsx | 9 ++++----- web/src/pages/CreateRpaOperation.tsx | 9 ++++----- web/src/pages/DriftsList.tsx | 6 +++--- web/src/pages/EventsOverlaps.tsx | 20 ++++++++++---------- web/src/pages/EventsStreamline.tsx | 4 ++-- web/src/pages/Insights.tsx | 20 ++++++++++---------- web/src/pages/Locks.tsx | 14 +++++++------- web/src/pages/RpaUsage.tsx | 18 +++++++++--------- web/src/pages/VersionCompliance.tsx | 12 ++++++------ 22 files changed, 102 insertions(+), 91 deletions(-) diff --git a/web/index.html b/web/index.html index ea55d91..37dabb2 100644 --- a/web/index.html +++ b/web/index.html @@ -12,10 +12,6 @@ - - - -
diff --git a/web/package-lock.json b/web/package-lock.json index b6e5c51..1743c57 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,6 +8,8 @@ "name": "tracker-web", "version": "1.0.0", "dependencies": { + "@fontsource-variable/spline-sans": "^5.3.0", + "@fontsource-variable/spline-sans-mono": "^5.3.0", "@fortawesome/fontawesome-svg-core": "^6.7.2", "@fortawesome/free-brands-svg-icons": "^6.7.2", "@fortawesome/free-solid-svg-icons": "^6.7.2", @@ -746,6 +748,24 @@ } } }, + "node_modules/@fontsource-variable/spline-sans": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/spline-sans/-/spline-sans-5.3.0.tgz", + "integrity": "sha512-a0EVY1u6qvI5uk66ImOKbh3QD8y3LtMpuqMMyN4OB9eJwivKPXNTn8rJUB+YLrJ5LxS85vqSmG1xeYG/mF8MDw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/spline-sans-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/spline-sans-mono/-/spline-sans-mono-5.3.0.tgz", + "integrity": "sha512-dH/qBj/MCE4ObiuCwlA9X1o3AftMO9dwfFgoRVYjy6VCPYkpWAlEtK/RjeyICgsR8rW763IAQARskVgShBY5JA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.7.2", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", diff --git a/web/package.json b/web/package.json index e1274e2..af723cd 100644 --- a/web/package.json +++ b/web/package.json @@ -20,6 +20,8 @@ "test:watch": "vitest" }, "dependencies": { + "@fontsource-variable/spline-sans": "^5.3.0", + "@fontsource-variable/spline-sans-mono": "^5.3.0", "@fortawesome/fontawesome-svg-core": "^6.7.2", "@fortawesome/free-brands-svg-icons": "^6.7.2", "@fortawesome/free-solid-svg-icons": "^6.7.2", diff --git a/web/src/components/EventDetailsModal.tsx b/web/src/components/EventDetailsModal.tsx index 512cce3..144fbe4 100644 --- a/web/src/components/EventDetailsModal.tsx +++ b/web/src/components/EventDetailsModal.tsx @@ -506,10 +506,10 @@ export default function EventDetailsModal({ event, onClose }: EventDetailsModalP )} {isEditing ? ( - setEditedEvent({ ...editedEvent, title: e.target.value })} /> ) : (<> -

+

{editedEvent.title}

{editedEvent.attributes.service}

diff --git a/web/src/components/FormPanel.tsx b/web/src/components/FormPanel.tsx index b1ddb6f..0454c60 100644 --- a/web/src/components/FormPanel.tsx +++ b/web/src/components/FormPanel.tsx @@ -103,7 +103,7 @@ export default function FormPanel({ {icon}
-

+

{title}

{subtitle && ( diff --git a/web/src/index.css b/web/src/index.css index 6963fef..1f574c0 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -192,9 +192,6 @@ --color-hud-error: rgb(var(--hud-error)); --color-hud-warning: rgb(var(--hud-warning)); - --font-grotesk: 'Plus Jakarta Sans', sans-serif; - --font-mono: 'IBM Plex Mono', 'JetBrains Mono', monospace; - --font-sans: 'Plus Jakarta Sans', sans-serif; --text-fs-100: 0.75rem; --text-fs-100--line-height: 1rem; @@ -446,8 +443,7 @@ body { background-color: rgb(var(--hud-bg)); color: rgb(var(--hud-on-surface)); - font-family: 'Plus Jakarta Sans', sans-serif; - font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11'; + font-family: var(--font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } diff --git a/web/src/main.tsx b/web/src/main.tsx index 18baebf..f996fed 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -4,6 +4,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ThemeProvider } from './contexts/ThemeContext' import { CreatePanelProvider } from './contexts/CreatePanelContext' import App from './App' +import '@fontsource-variable/spline-sans' +import '@fontsource-variable/spline-sans-mono' import './index.css' import { config } from './config' diff --git a/web/src/pages/CatalogDependencies.tsx b/web/src/pages/CatalogDependencies.tsx index 84ea245..8fdf30d 100644 --- a/web/src/pages/CatalogDependencies.tsx +++ b/web/src/pages/CatalogDependencies.tsx @@ -565,7 +565,7 @@ export default function CatalogDependencies() {
-

+

Global Dependencies

@@ -590,7 +590,7 @@ export default function CatalogDependencies() {

{label}

- {value} + {value} {icon}
@@ -801,7 +801,7 @@ export default function CatalogDependencies() {
-

+

Dependency Graph

@@ -843,7 +843,7 @@ export default function CatalogDependencies() { ) : (

-

+

Dependencies List View

@@ -996,7 +996,7 @@ export default function CatalogDependencies() {

- + {totalDeps} {totalDeps > 0 && ( diff --git a/web/src/pages/CatalogDetail.tsx b/web/src/pages/CatalogDetail.tsx index 41454f7..bba140b 100644 --- a/web/src/pages/CatalogDetail.tsx +++ b/web/src/pages/CatalogDetail.tsx @@ -879,7 +879,7 @@ export default function CatalogDetail({ serviceNameProp, onClose }: CatalogDetai
-

+

{service.name}

diff --git a/web/src/pages/CatalogTable.tsx b/web/src/pages/CatalogTable.tsx index 3bf0ccc..fcac19a 100644 --- a/web/src/pages/CatalogTable.tsx +++ b/web/src/pages/CatalogTable.tsx @@ -242,7 +242,7 @@ export default function CatalogTable() {

Filters @@ -490,7 +490,7 @@ export default function CatalogTable() {

Catalog

diff --git a/web/src/pages/CreateCatalog.tsx b/web/src/pages/CreateCatalog.tsx index 96b4f0e..9e1bb79 100644 --- a/web/src/pages/CreateCatalog.tsx +++ b/web/src/pages/CreateCatalog.tsx @@ -200,7 +200,7 @@ export default function CreateCatalog({ asPanel = false, onClose, onSuccess }: C const SectionHeader = ({ icon, title, color }: { icon: ReactNode; title: string; color?: string }) => (
{icon} -

{title}

+

{title}

) @@ -483,7 +483,7 @@ export default function CreateCatalog({ asPanel = false, onClose, onSuccess }: C } title="SLA" color={hud.tertiary} />
-

Service Level Agreement

+

Service Level Agreement