npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

design-j6n

v0.1.0

Published

Design system personnel — Foulée. theme.css + theme.js, sans dépendance ni build.

Downloads

139

Readme

design-j6n

Design system personnel — Foulée. Nature et trail pour les couleurs, vert terminal pour l'énergie. Deux fichiers (theme.css + theme.js), aucune dépendance, aucun build. Utilisable en HTML statique, Angular ou React. Deux thèmes visuels partagent les mêmes composants : nature (par défaut) et moderne (gris/bleu) — voir Thèmes.

  • Préfixe : --j6n-* pour les variables, .j6n-* pour les classes, data-j6n-* pour les hooks JS
  • Typographie : Sora + IBM Plex Mono
  • Thème clair et sombre, à parité
  • Couleurs en oklch() (Chrome/Edge 111+, Safari 15.4+, Firefox 113+)
  • theme.js s'appuie sur les éléments natifs (<details>, <dialog>, Popover API) plutôt que de réimplémenter le comportement en JS — fonctionne sans lui pour l'accordéon et la modale, requis pour les onglets, le menu déroulant, le combobox et les bandeaux mémorisés

Installation

Via npm :

npm install design-j6n

Ou copier theme.css (styles) et theme.js (comportements) directement dans le projet. Les polices sont importées par theme.css lui-même.

HTML statique

<link rel="stylesheet" href="theme.css">
<script src="theme.js"></script>

Angular

// angular.json → architect.build.options
"styles": ["src/theme.css", "src/styles.css"],
"scripts": ["src/theme.js"]

React / Vite / CRA

// main.jsx ou index.js
import 'design-j6n/theme.css'; // ou './theme.css' si copié dans le projet
import 'design-j6n/theme.js';  // ou './theme.js'

En SPA, si du contenu est injecté dynamiquement après le chargement initial (ex. un composant React monté plus tard), appeler j6n.init(monConteneur) pour activer les comportements sur ce sous-arbre.

Thème sombre

Sans rien faire, le thème suit prefers-color-scheme. Pour forcer un mode, poser l'attribut sur <html> :

document.documentElement.dataset.theme = 'dark'; // ou 'light'

Thèmes

Deux thèmes visuels, mêmes composants et mêmes classes .j6n-* — seuls les tokens (couleurs + polices) changent. Par défaut : nature (vert terrain, Sora + IBM Plex Mono). Pour passer au thème moderne (bleu/gris, Inter + JetBrains Mono), poser l'attribut sur <html> :

document.documentElement.dataset.j6nTheme = 'moderne'; // ou retirer l'attribut pour revenir à nature
<html data-j6n-theme="moderne">

Se combine librement avec data-theme (clair/sombre/auto) — les deux attributs sont indépendants, donc les quatre combinaisons (nature clair/sombre, moderne clair/sombre) fonctionnent, y compris prefers-color-scheme sans data-theme explicite. Les couleurs sémantiques (succès/attention/erreur) et les couleurs de marque tierce (.j6n-connect) restent identiques d'un thème à l'autre ; chaque token du thème moderne a été vérifié au même seuil WCAG que nature (4.5:1 texte, 3:1 frontière fonctionnelle).

Tokens

| Groupe | Variables | | --- | --- | | Marque | --j6n-accent, --j6n-accent-strong, --j6n-accent-soft, --j6n-pine, --j6n-pine-soft | | Sémantique | --j6n-success, --j6n-warning, --j6n-danger, --j6n-info | | Surfaces | --j6n-bg, --j6n-surface, --j6n-surface-2, --j6n-border, --j6n-border-strong (décoratif), --j6n-border-interactive (3:1 garanti — champ, tag, tuile de choix, piste d'interrupteur…) | | Texte | --j6n-text, --j6n-text-muted, --j6n-text-invert | | Typo | --j6n-font-sans, --j6n-font-mono, --j6n-text-xs--j6n-text-3xl | | Cadence | --j6n-space-1 (4px) → --j6n-space-9 (96px) | | Formes | --j6n-radius-sm (4) · -md (8) · -lg (14) · -pill | | Élévation | --j6n-shadow-sm, --j6n-shadow-md, --j6n-ring | | Mouvement | --j6n-ease, --j6n-dur-1 (120ms) · -2 (220ms) · -3 (420ms) |

Composants

Actions

| Classe | Variantes | | --- | --- | | .j6n-btn | --secondary --pine --ghost --sm --lg --block --icon, état .is-loading (spinner) | | .j6n-segmented | groupe de radios stylé, une seule sélection visible | | .j6n-toggle | interrupteur on/off (__track) | | .j6n-tag | étiquette filtrable, .is-active, __remove |

Formulaires

| Classe | Variantes | | --- | --- | | .j6n-field | avec .j6n-label, .j6n-input, .j6n-select, .j6n-textarea, .j6n-hint (--valid/--danger), .j6n-check, --icon (+ __icon), __count | | .j6n-fieldset / .j6n-legend | groupe de champs (radio/case à cocher) | | .j6n-choice-tile | radio/case à cocher en tuile bordée, sélection via :has() | | .j6n-search | champ + bouton fusionnés (__input, __btn) | | .j6n-password | champ + bascule visibilité (__toggle), .j6n-password__meter (force) | | .j6n-range-field / .j6n-range | curseur avec piste remplie et valeur affichée | | .j6n-upload | zone de glisser-déposer, .is-dragover, __list/__item | | .j6n-combobox | saisie avec suggestions filtrées (__list, __option) |

Navigation

| Classe | Variantes | | --- | --- | | .j6n-header | avec .j6n-brand, .j6n-nav, .j6n-nav__link.is-active, .j6n-nav__toggle (menu mobile) | | .j6n-breadcrumb | fil d'Ariane, [aria-current="page"] | | .j6n-tabs__list / __tab / __panel | onglets accessibles (flèches clavier) | | .j6n-dropdown__menu | menu déroulant (popover natif), __item, __divider | | .j6n-sidemenu | navigation verticale, __link.is-active, __sublist | | .j6n-toc | sommaire à suivi de défilement | | .j6n-pagination | [aria-current="page"], .is-ellipsis, .is-disabled | | .j6n-skiplink | lien d'évitement, visible au focus | | .j6n-stepper | étapes numérotées, .is-current / .is-done | | .j6n-tile | tuile de navigation, --horizontal | | .j6n-footer | pied de page multi-colonnes (__grid, __col, __bottom) |

Retours

| Classe | Variantes | | --- | --- | | dialog.j6n-modal | modale (<dialog> natif), __header, __body, __footer | | [data-tooltip] | infobulle CSS, data-tooltip-pos="bottom" | | .j6n-notice | bandeau pleine largeur, fermeture mémorisée | | .j6n-accordion | <details> stylés, __trigger, __panel | | .j6n-callout | mise en avant, --pine | | .j6n-consent | bandeau de consentement générique, fermeture mémorisée | | .j6n-alert | --success --warning --danger, __close | | .j6n-toast | pile de toasts via l'API JS j6n.toast() |

Données & contenu

| Classe | Variantes | | --- | --- | | .j6n-card | --interactive, plus .j6n-card__title, .j6n-card__meta | | .j6n-stat | __value, __unit, __label — chiffres tabulaires | | .j6n-badge | --accent --pine --solid, plus .j6n-badge__dot | | .j6n-table | .j6n-table-wrap, [data-numeric], .j6n-table__sort (tri au clic) | | .j6n-quote | citation avec __cite | | .j6n-highlight | mise en exergue de texte | | .j6n-download | lien de téléchargement, __ext, __name, __meta | | .j6n-user-menu__trigger | avatar + menu (réutilise .j6n-dropdown__menu) | | .j6n-connect | connexion à un service externe, --strava --garmin | | .j6n-unit-switch | bascule d'unités (réutilise .j6n-segmented) | | .j6n-pre | blocs de code, toujours sombres ; .tok-key .tok-str .tok-com |

Utilitaires

.j6n-container .j6n-stack .j6n-row .j6n-grid .j6n-layout (mise en page à sommaire latéral) .j6n-eyebrow .j6n-muted .j6n-mono .j6n-divider-run .j6n-skeleton .j6n-chevron .j6n-close .j6n-spinner .j6n-sr-only (contenu réservé aux lecteurs d'écran)

Contrôles & dynamisme

theme.js amorce en posant data-j6n="true" sur <html>, puis active chaque composant dynamique via un attribut dédié — jamais en lisant les classes CSS, pour garder style et comportement séparés.

| Attribut / élément | Composant | | --- | --- | | data-j6n-js="accordion-group" | ferme les autres <details> du groupe à l'ouverture d'un item | | data-j6n-js="tabs" | onglets — clic + flèches clavier | | data-j6n-modal-open="id" / data-j6n-modal-close | ouvre/ferme un <dialog class="j6n-modal"> | | [popovertarget] | positionne le menu déroulant sous son déclencheur (le reste est natif) | | data-j6n-js="combobox" | filtre, navigation clavier, sélection | | [data-tooltip] | ajoute le support tactile (tap pour afficher/masquer) | | data-j6n-js="password" + data-j6n-password-toggle | bascule visibilité + indicateur de force | | data-j6n-js="range" | remplissage de piste + valeur affichée | | data-j6n-js="upload" | glisser-déposer + liste de fichiers | | data-j6n-dismiss="clé" + data-j6n-dismiss-target="id" | ferme et mémorise en localStorage (notice, consentement) | | data-j6n-js="toc" | surligne le lien courant au défilement (IntersectionObserver) | | data-j6n-js="table-sort" | tri des lignes au clic sur un .j6n-table__sort | | data-j6n-js="menu-toggle" | ouvre/ferme le menu mobile du header | | data-j6n-js="unit-switch" | diffuse un évènement j6n:unit-change |

API publique : j6n.init(root) (réactiver un sous-arbre injecté dynamiquement), j6n.toast(message, { tone, duration }), j6n.openModal(id), j6n.closeModal(id).

Exemple

<main class="j6n-container j6n-stack">
  <p class="j6n-eyebrow">Dernière sortie</p>
  <article class="j6n-card j6n-card--interactive">
    <p class="j6n-card__meta">SAM. 29 AOÛT · 07:12</p>
    <h3 class="j6n-card__title">Crête des Ayes</h3>
    <div class="j6n-stat">
      <span class="j6n-stat__value">18,4<span class="j6n-stat__unit"> km</span></span>
      <span class="j6n-stat__label">Distance</span>
    </div>
    <span class="j6n-badge j6n-badge--pine">Sentier</span>
  </article>
  <button class="j6n-btn">Enregistrer</button>
</main>

demo/ couvre l'ensemble des composants sur six pages, à ouvrir directement dans un navigateur (aucun serveur requis) :

| Page | Contenu | | --- | --- | | demo/index.html | sommaire, tokens, règles | | demo/actions.html | boutons, contrôle segmenté, interrupteur, étiquettes | | demo/formulaires.html | champs, recherche, mot de passe, curseur, import, combobox | | demo/navigation.html | fil d'Ariane, onglets, menu déroulant, pagination, étapes, tuiles, pied de page | | demo/retours.html | alertes, toasts, infobulles, modale, accordéon, mise en avant, consentement | | demo/donnees.html | carte, tableau triable, citation, téléchargement, menu utilisateur, connexion externe |

Chaque exemple des cinq pages de composants a un bloc Résultat / Code avec un simulateur de largeur (Mobile / Tablette / Large) et un bouton « Copier », dans l'esprit des pages de démonstration de systeme-de-design.gouv.fr. Ça vit dans demo/showcase.css + demo/showcase.js — un outil pour ces pages de démo, pas une partie du design system livré (rien à installer dans un vrai projet).

Règles

À faire :

  • passer par les variables --j6n-*, jamais de valeur en dur ;
  • un seul usage de l'accent par écran, réservé à l'action principale (les chips de marque --strava/--garmin de .j6n-connect font exception : elles identifient un tiers, pas l'action principale de l'écran) ;
  • cibles tactiles à 44 px minimum ;
  • --j6n-font-mono pour les valeurs mesurées, les libellés en petites capitales et le code ;
  • séparer comportement et style : les hooks JS sont des attributs data-j6n-*, jamais des classes .j6n-*.

À éviter :

  • dégradés de fond et ombres floues ;
  • le mono pour du texte courant ;
  • des px hors de l'échelle de cadence.

Accessibilité (RGAA / WCAG 2.1 AA)

Un audit du code a été fait et corrigé sur cette base — pas seulement une relecture visuelle :

  • Contraste : chaque paire texte/fond et chaque frontière fonctionnelle (champ, tuile de choix, tag, piste d'interrupteur…) a été calculée (conversion oklch → luminance relative WCAG), pas estimée à l'œil. Ça a fait remonter et corrigé de vrais échecs : --j6n-border-strong ne passait pas 3:1 sur fond clair ni sombre (d'où le nouveau --j6n-border-interactive, réservé aux frontières qui portent seules une information — les liserés purement décoratifs gardent --j6n-border-strong) ; --j6n-danger et --j6n-pine/--j6n-pine-soft tombaient sous 4.5:1 en thème sombre faute d'y avoir jamais été redéfinis ; l'ancien anneau de focus translucide n'atteignait que 1.3:1 en clair — remplacé par un anneau plein à deux tons (écart + trait), qui reste lisible même sur un bouton déjà coloré.
  • Clavier : chaque composant interactif est utilisable sans souris, y compris ceux avec un état caché (menu déroulant, combobox, onglets, accordéon, modale). Un vrai bug a été trouvé et corrigé au passage : l'étiquette .j6n-tag imbriquait un <span> de retrait dans le bouton principal — inatteignable au Tab et dupliquant le nom accessible du bouton. C'est maintenant deux boutons frères.
  • Focus : la modale (<dialog> natif) piège le focus et le restitue au déclencheur nativement ; les menus (Popover API) rendent le focus au bouton qui les a ouverts à la fermeture.
  • ARIA : posé dans le HTML source de chaque page, pas seulement calculé par theme.js au chargement — un analyseur statique (SonarQube, axe…) ne voit que le balisage, jamais le DOM que le JS construit ; theme.js reconfirme les mêmes attributs à l'exécution, en filet de sécurité pour qui oublierait de les écrire. aria-expanded/aria-haspopup sur les déclencheurs de menu (le Popover API ne les écrit pas lui-même), role="listbox"/"option" + aria-selected/aria-controls du combobox, aria-current="page" sur le lien de navigation actif, association automatique champ ↔ message d'aide/erreur via aria-describedby (initFieldDescriptions), annonce discrète (aria-live) du résultat d'un tri de tableau. Le combobox suit le motif officiel du WAI-ARIA (role="option" sur des <div>, pas de <option> natif) — c'est volontaire : aucun élément natif ne combine filtrage libre à la frappe et liste stylée (<select> ne filtre pas, <datalist> ne se stylise pas). Un linter générique « préfère le natif à ARIA » peut le signaler ; c'est un faux positif connu sur ce motif, documenté dans demo/formulaires.html.
  • Structure : lien d'évitement, hiérarchie de titres sans saut de niveau, un seul <main> par page, <nav> distingués par aria-label quand il y en a plusieurs, légende de tableau (.j6n-sr-only), autocomplete sur les champs d'identité (email, mot de passe, pseudo).
  • Mouvement : prefers-reduced-motion déjà pris en compte pour tout le système ; l'indicateur de chargement (.j6n-spinner) garde un mouvement minimal même dans ce cas, un indicateur figé ne voulant plus rien dire.

Limite honnête : ceci est un audit de code, pas une certification RGAA. Une conformité déclarée demande une méthodologie que je ne peux pas reproduire ici — échantillon de pages, tests réels au lecteur d'écran (NVDA, JAWS, VoiceOver), navigation clavier bout en bout par une personne, et la déclaration d'accessibilité elle-même. Avant une mise en production, fais au moins un passage clavier seul (Tab/Échap/flèches sur chaque page) et un passage avec un lecteur d'écran sur les composants les plus riches (combobox, modale, menu déroulant, tableau triable).

Publication

Chaque tag vX.Y.Z poussé sur master déclenche une Release GitHub automatique (.github/workflows/release.yml) : le projet est d'abord vérifié (ci.yml, réutilisé), puis empaqueté (theme.css + theme.js + README.md + LICENSE) en .zip et .tar.gz attachés à la Release, avec des notes générées depuis les commits/PR depuis le tag précédent.

git tag v1.0.0
git push origin v1.0.0
  • scripts/validate.sh — les mêmes vérifications tournent à chaque push/PR (ci.yml) et avant chaque release ; à lancer en local avant de taguer.
  • scripts/package.sh — assemble le paquet ; utilisable en local pour en vérifier le contenu (VERSION=0.0.0-test scripts/package.sh).

Démo en ligne

netlify.toml publie tout le dépôt (les pages de demo/ référencent ../theme.css/../theme.js, donc la racine doit être servie aussi) et redirige / vers demo/index.html. Connecter le dépôt sur app.netlify.com suffit — Netlify lit netlify.toml automatiquement, aucune configuration supplémentaire.

Licence

MIT.