Communitygithub.com

OpenLabs-so/oa-design

Le langage de design d'Open Analytics dans un seul fichier, conçu pour les agents IA et les humains qui les dirigent.

Qu'est-ce que oa-design ?

Ce skill fournit le langage de design complet d'Open Analytics (OA) sous forme d'un fichier unique, optimisé pour être utilisé par des agents IA comme Claude Code ou Cursor, ainsi que par les développeurs. Il définit l'ensemble des règles visuelles, des tokens CSS (couleurs, rayons, typographie, ombres), des composants (boutons, cartes, modales, toasts) et des principes d'animation et de mise en page qui constituent l'identité de la plateforme OA. Le fichier est conçu pour être copié-collé dans le contexte d'un agent ou déposé dans un dépôt en tant que fichier de règles, permettant de générer des interfaces utilisateur 'dans le style OA' avec une cohérence parfaite. En plus de ce fichier compilé, le dépôt contient également une version skill complète avec des recettes par composant et une interface en ligne de commande (CLI) pour une intégration plus poussée avec des agents capables de lire les skills. L'approche se résume à des surfaces blanches aux coins arrondis continus, reposant sur un fond gris discret, dessinées avec une seule encre et animées par un seul ressort.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/OpenLabs-so/oa-design/tree/HEAD

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

DESIGN-SKILL.md

Le langage de design d'Open Analytics dans un seul fichier, écrit pour les agents IA et les humains qui les dirigent. Chaque valeur ici est livrée dans le produit : codes hexadécimaux, constantes de ressort, rayons, assouplissements. Collez ce fichier dans le contexte de votre agent (ou placez-le dans votre dépôt en tant que fichier de règles) et demandez des écrans « dans le style OA ».

Il s'agit de l'édition compilée en un seul fichier, conçue pour être copiée. Le même langage est également livré dans ce dépôt en tant que skill d'agent propre (skills/oa-design/ : recettes par composant avec le source complet vérifié par type) et en tant que CLI (npx getopen-design add <composant>) ; privilégiez ces versions pour Claude Code et les autres agents capables de lire les skills.

Le look en une phrase : des surfaces blanches aux coins à courbure continue, reposant sur une scène grise silencieuse, dessinées avec une seule encre, mues par un seul ressort.


1. Les dix règles

  1. Une seule encre, tout est dérivé. Tout le système neutre est une couleur mélangée à de la transparence à des pourcentages fixes. Ne choisissez jamais un second gris.
  2. Deux couches, et l'écart est la page. Les surfaces sont un cadre blanc contenant un insert gris en retrait ; les sections sont des plaques et l'arrière-plan de la page entre elles est le seul séparateur. Pas de règles horizontales.
  3. Des squircles pour les surfaces, des pilules pour les actions. Les cartes ont des coins à courbure continue ; tout ce qui est cliquable qui n'est pas une carte est une pilule.
  4. Une famille de ressorts. Sept ressorts nommés couvrent tout le produit. N'en inventez pas un huitième.
  5. Le chrome n'attend jamais. La mise en page et les titres s'affichent instantanément ; seuls les changements de données à partir d'un squelette dont les pixels correspondent, arrivant par un flou, pas par un affichage soudain.
  6. Le poids s'arrête à 500. Pas de gras. La hiérarchie provient de la taille, de la couleur et de l'espacement.
  7. Un accent, dépensé à un seul endroit. Un seul bleu pour les actions primaires et la ligne de graphique primaire ; les couleurs sémantiques sont des teintes de texte, pas des remplissages.
  8. Les états sont des pilules, les événements se terminent seuls. Les conditions permanentes sont rendues sous forme de bandes non désactivables qui vivent exactement aussi longtemps que l'état ; les résultats ponctuels sont des toasts qui disparaissent seuls.
  9. Le texte fait partie du design. Utilisation de la casse des phrases ; les boutons disent ce qui se passe ; les erreurs nomment la cause et la solution, sans blâme.
  10. Seuil de qualité, toujours. Anneaux de focus, role="status", décorations aria-hidden, prefers-reduced-motion, et aucun défilement horizontal de page.

2. Tokens

Clair

:root {
  --ink: #292929;

  --background: #f6f6f6;     /* la scène grise entre les plaques */
  --card: #ffffff;
  --popover: #ffffff;

  /* neutres dérivés de l'encre : les pourcentages SONT le système */
  --border: color-mix(in srgb, var(--ink) 12%, transparent);
  --input:  color-mix(in srgb, var(--ink) 14%, transparent);
  --accent: color-mix(in srgb, var(--ink) 5%, transparent);  /* lavis de survol */
  --muted:  color-mix(in srgb, var(--ink) 5%, transparent);
  --muted-foreground: #6d6d6d;
  --foreground: var(--ink);

  --primary: #305dde;             /* l'unique accent */
  --primary-foreground: #ffffff;
  --ring: #3ba6f1;

  --secondary: #e9e9e9;           /* contrôles gris plats, sans bordure */
  --secondary-foreground: var(--ink);

  /* sémantiques, en tant que teintes TEXT sur fond clair (échelle Tailwind, ligne 700) */
  --destructive: var(--color-red-500);
  --destructive-foreground: var(--color-red-700);
  --success-foreground: var(--color-emerald-700);
  --warning-foreground: var(--color-amber-700);
  --info-foreground: var(--color-blue-700);

  --chart-1: #296FF0;             /* ligne de graphique primaire */
  --radius: 0.625rem;
}

Besoin d'un séparateur plus fort ? Augmentez le pourcentage d'encre (le produit utilise seulement 4, 5, 10, 12, 14). Les couleurs sémantiques vont sur les mots et les points de 8px, jamais sur les remplissages de panneaux.

Sombre

Même recette, réencrée, avec deux changements systématiques : les pourcentages de cheveux augmentent de 2 points, et le texte sémantique passe de la ligne 700 à la ligne 400.

.dark {
  --ink: #ededed;
  --background: #191919;
  --card: #212121;
  --popover: #242424;
  --border: color-mix(in srgb, var(--ink) 14%, transparent);
  --input:  color-mix(in srgb, var(--ink) 16%, transparent);
  --muted-foreground: #969696;
  --secondary: #2e2e2e;
  --primary: #296FF0;
  --destructive-foreground: var(--color-red-400);
  --success-foreground: var(--color-emerald-400);
}

Rayon

Une base, multipliée ; jamais de rayon sur mesure :

--radius: 0.625rem;  /* sm ×0.6, md ×0.8, lg ×1, xl ×1.4,
                        2xl ×1.8, 3xl ×2.2, 4xl ×2.6 */

rounded-full est réservé aux pilules. Les surfaces de type squircle ont leurs propres rayons plus grands (section 4).

Typographie

  • Inter Tight, variable, poids 300 à 500, auto-hébergé (les CDN de polices échouent dans les builds ; conservez le woff2 dans le dépôt).
  • Geist Mono pour code, kbd, samp, pre, les touches et les extraits.
  • Le plafond de poids est l'identité : rien de plus lourd que 500 n'est rendu. La hiérarchie provient de la taille, de text-foreground/80 vs text-muted-foreground et de l'espacement.
  • Titres : font-medium tracking-tight ; les titres marketing ajoutent text-wrap: balance.
  • Données : tabular-nums sur tout nombre dans une colonne ou mis à jour sur place.
  • La voix UI par défaut est 14px ; les lignes secondaires 12px, avec leading-5/leading-6.

Ombres

L'élévation est binaire :

/* carte au repos */    box-shadow: 0 1px 2px rgba(0,0,0,0.06);
/* chrome flottant */ box-shadow: 0 1px 2px rgba(0,0,0,0.08),
                                  0 8px 24px rgba(0,0,0,0.08);

Discipline de fond

Le body est blanc (le dépassement montre le corps ; le gris qui apparaît derrière un écran blanc donne l'impression d'une page différente). Les écrans qui veulent la scène grise peignent leur propre décor. La racine obtient overflow-x: clip.


3. Mise en page

Plaques. Les sections sont des panneaux autonomes ; l'arrière-plan de la page qui apparaît entre elles est la seule limite. Les espaces verticaux sont généreux (gap-12 sm:gap-20 marketing, gap-6 dans l'application), le remplissage latéral plus serré (px-4 sm:px-6). Les éléments frères sont espacés par le gap du parent, jamais par des marges par élément. Pas de <hr>, pas de border-b entre les sections.

Largeurs par portée : marketing 76rem (6.5xl), pages du site de l'application max-w-6xl, écrans de liste/sélection max-w-5xl, surfaces de lecture ~65ch. Centré, mx-auto.

Anatomie d'une page d'application :

en-tête fixe (h-14) → conteneur pt-14
  pilule flottante optionnelle (fixed top-16, centrée)
  <main class="px-4 pb-36 pt-8 sm:px-6 bg-white">
    ligne de titre : h1 text-xl font-medium tracking-tight + UN contrôle à droite
    bande(s) d'avis optionnelle(s)
    plaques : flex flex-col gap-6

La ligne de titre contient exactement un contrôle. pb-36 maintient le contenu éloigné du bord de la fenêtre.

Grilles : grid gap-3, colonnes réactives (grid-cols-2 sm:grid-cols-4 pour les statistiques). Les cartes partagent une hauteur de contenu fixe et défilent en interne ; la ligne de base de la grille ne se brise jamais.

Réactif : conception à 390px et 1280px. Les paires horizontales s'empilent sous sm: avec l'action en dernier. Le contenu large défile dans son propre conteneur overflow-x-auto ; la page ne défile jamais horizontalement.

Paramètres : rail d'onglets étroit + colonne de contenu des panneaux de section ; chaque section s'ouvre avec un bloc d'en-tête (titre + une phrase en gris, action unique optionnelle à droite). Onglets à lien profond via ?tab=<id>.


4. Composants

La surface squircle

Coins à courbure continue via un clip-path shape() CSS paramétré par deux variables, avec corner-shape: squircle là où c'est pris en charge :

.squircle {
  border-radius: 26px;
  --card-clip-radius: 14px;
  --card-clip-handle: 2.25px;
  clip-path: shape(
    from var(--card-clip-radius) 0px,
    line to calc(100% - var(--card-clip-radius)) 0px,
    curve to 100% var(--card-clip-radius)
      with calc(100% - var(--card-clip-handle)) 0px / 100% var(--card-clip-handle),
    line to 100% calc(100% - var(--card-clip-radius)),
    curve to calc(100% - var(--card-clip-radius)) 100%
      with 100% calc(100% - var(--card-clip-handle)) / calc(100% - var(--card-clip-handle)) 100%,
    line to var(--card-clip-radius) 100%,
    curve to 0px calc(100% - var(--card-clip-radius))
      with var(--card-clip-handle) 100% / 0px calc(100% - var(--card-clip-handle)),
    line to 0px var(--card-clip-radius),
    curve to var(--card-clip-radius) 0px
      with 0px var(--card-clip-handle) / var(--card-clip-handle) 0px,
    close
  );
  corner-shape: squircle;
}
@media (min-width: 640px) {
  .squircle { border-radius: 50px; --card-clip-radius: 20px; --card-clip-handle: 3px; }
}

La mini carte

squircle externe : bg-card, border, p-1, ombre au repos
├── bande d'en-tête (pb-2 pl-3.5 pr-2 pt-1.5) :
│     icône atténuée 16px + titre text-sm font-medium text-foreground/80
│     puce optionnelle sur l'axe central du titre
│     "Voir tout ›" (text-xs muted ; survol : lavis d'accent, flèche glisse de 2px)
└── squircle interne : bg #f6f6f6, border, rounded-[22px] sm:rounded-[44px],
      hauteur FIXE (h-44 = cinq lignes de 32px), overflow hidden

Les cartes ne grandissent jamais avec les données ; les listes plus longues défilent à l'intérieur. Le débordement montre une puce de chevron de 24px en bas à droite, avec un mouvement de y:[0,2.5,0] (boucle 1.4s easeInOut), s'estompant (0.18s, échelle 0.7) à la fin du défilement. Lignes : 32px, survol avec lavis d'accent à pleine largeur, valeurs tabular-nums.

Boutons

Pilules, font-medium, physiquement enfoncées : active:translate-y-px active:scale-[0.98].

  • primaire : bleu d'accent avec un biseau (fond/bordure mélangés vers un indigo profond, inset 0 1px 0 rgba(255,255,255,0.22) en haut, bordure sombre en bas) ; le survol éclaircit vers le primaire pur.
  • secondaire : --secondary plat, bordure transparente, le survol mélange 5% d'encre. Le cheval de bataille.
  • fantôme : texte atténué uniquement. destructeur : remplissage rouge, uniquement à l'étape de confirmation.
  • Tailles h-9 / h-8 / h-7 ; le chrome de l'application utilise surtout les petites.
  • En chargement, le libellé est conservé et un spinner annulaire de 14px (border-2 border-current border-t-transparent) est ajouté ; la largeur ne saute jamais.
  • focus-visible:ring-[3px] dans --ring/50. Jamais supprimé.

Panneaux déroulants (menus, sélecteurs, commutateurs)

Pilule de déclenchement → panneau flottant (ombre flottante, bordure, blanc, rounded-2xl+), entrée par ressort à PANEL (550/38) avec un petit décalage y/échelle, sortie plus rapide. Éléments de 32px rounded-lg avec survol en lavis d'accent ; la surbrillance active est un seul élément partagé qui se déplace (ressorts layoutId ou largeur/hauteur), jamais de repeints par élément. Fermeture sur pointerdown à l'extérieur et Escape.

Modales

Le fond se dissout en ~0.15s ; le panneau entre par ressort à POP (400/26) depuis opacity 0, scale .96, sort à 380/28. Les dialogues à plusieurs étapes conservent un seul panneau dont la hauteur s'anime vers la taille mesurée de chaque étape (LAYOUT 550/40 + ResizeObserver) pendant que les étapes glissent (x: ±12px, popLayout, 0.16s easeOut fades). Pied de page : bande h-12, Retour (secondaire xs) + primaire xs, aligné à droite.

Barre d'onglets

La pilule active est une fenêtre fixe ; les libellés glissent derrière elle. Le libellé entrant vient de la direction de déplacement à 110% de sa propre largeur ; le libellé sortant sort à 130px fixes de l'autre côté (un pourcentage échoue lorsque la pilule grandit vers un mot plus long). La largeur/hauteur de la pilule suit le libellé mesuré sur les ressorts LAYOUT. Opacité totale tout au long ; le masque fait le travail.

Squelettes

  1. Le chrome n'attend jamais ; seule la zone de données est un squelette.
  2. Correspondance pixel : une barre se trouve dans le même emplacement de hauteur de ligne que le texte qu'elle représente (flex h-6 items-center entourant une barre h-3) ; l'échange ne déplace rien.
  3. Les données arrivent par le focus : une animation unique de opacity ~0.4 + blur(4px) vers la netteté. Une animation, pas une transition (le contenu est monté déjà final ; une transition n'a pas de valeur précédente). Gardez filter: blur(0) après.

Barre : rounded bg-muted-foreground/15, balayage lent de la position de fond (2s linéaire infini, décalage de départ -1s pour que les barres soient déphasées).

Avis, états vides, la pilule flottante

Bande d'avis au-dessus du contenu qu'elle explique :

<div class="flex flex-col gap-2 rounded-2xl border border-border bg-card
            px-4 py-3 sm:flex-row sm:items-center sm:justify-between"
     role="status">
  <p class="text-sm leading-6 text-muted-foreground">
    <span class="font-medium text-foreground">Aucun événement pour l'instant.</span>
    Si l'extrait est actif, parcourez votre site et nous capterons la
    première page vue en quelques secondes.
  </p>
  <button class="…pilule secondaire…">Vérifier l'installation</button>
</div>

Une affirmation en gras, une phrase en gris, une action secondaire ; s'empile sous sm: ; vit exactement aussi longtemps que l'état ; pas de licenciement.

Pilule flottante pour les conditions permanentes dans toute l'application : fixe top-16, centrée, rounded-full bg-card py-1.5 pl-3 pr-4 text-xs font-medium, ombre flottante, point d'état de 8px (pulsant en cas d'urgence), entrée par ressort à BANNER (400/30) depuis y:-8, sortie 0.15s. La pilule entière est un lien vers l'endroit où l'état est résolu. Les pilules d'état ne se désactivent jamais d'elles-mêmes ; les pilules d'événement disparaissent après ~6s.

Toasts

Moins de 0.35s, assouplissement cubic-bezier(0.5, 1, 0.89, 1) :

  • Succès : pulsation scale 1 → 1.025 → 0.99 → 1 (0.32s).
  • Erreur : secousse x 0 → -3 → 3 → -3 → 0 px (0.28s).

Alterner les copies d'images clés paires/im.paires pour que les répétitions redémarrent.


5. Mouvement

NomValeurUtilisé pour
PANEL550 / 38déroulants, menus, tableaux, bascules
LAYOUT550 / 40hauteur/largeur mesurée, pilules coulissantes
POP400 / 26entrée de modale
POP_EXIT380 / 28sortie de modale
BANNER400 / 30pilules flottantes, bannières de page
FLICK900 / 50micro-mouvements d'icône
CHART300 / 25-30infobulles de graphique, réticule

Micro-fondu : 0.1s à 0.18s easeOut. Rien dans le chrome de l'application ne s'anime plus de 0.2s ; si cela semble lent, réduisez l'amortissement, n'augmentez pas la durée.

Mouvements signature : chorégraphie de hauteur mesurée (un panneau constant, étapes qui glissent), la surbrillance mobile (un seul élément actif partagé, jamais de repeints par élément), asymétrie entrée/sortie (les sorties sont toujours plus rapides), survol uniquement par couleur (plus au maximum un glissement de flèche de 2px ; les cartes ne se soulèvent ni ne se redimensionnent jamais), et au maximum une minuscule boucle ambiante par écran.

Règles : AnimatePresence pour tout ce qui se démonte ; respecter prefers-reduced-motion (les révélations se réduisent à visibles, les boucles s'arrêtent) ; ne jamais animer une mise en page non mesurée ; ne jamais lire l'horloge murale pendant le rendu (s'abonner à un magasin qui tique toutes les minutes).


6. Pages d'atterrissage

Mêmes tokens, s

…

[tronqué pour la traduction]

Skills associés