ISNAD Scientific Platform

Documentation Center
V1 · Référence officielle

Architecture

Handoff : refonte de la lecture du Corpus (ISNAD)

11 min de lectureMis à jour le 29 أكتوبر 2026
Sur cette page

Overview#

Refonte de l'expérience de lecture du corpus dans isnad-app-fr (Next.js App Router + Supabase + Tailwind). Trois problèmes rapportés par le propriétaire du produit :

  1. « Pas facile à lire » — la page d'un livre (/[locale]/corpus/[collectionId]/books/[bookId]) est un unique flux vertical de chapitres, sans repère de position.
  2. Aucun menu de navigation latéral qui mette en évidence le chapitre / sous-chapitre en cours de lecture, y compris pendant le défilement.
  3. Mélange arabe / français sur des pages censées être monolingues, et aucun réglage de police pour le lecteur (3 choix demandés).

Le paquet contient deux fichiers de design : la reconstitution 1:1 de l'état actuel, et la refonte proposée (bureau FR clair, bureau AR sombre RTL, mobile FR).

About the Design Files#

Les fichiers .dc.html de ce paquet sont des références de design réalisées en HTML — des prototypes qui montrent l'apparence et le comportement visés. Ce n'est pas du code de production à copier. Le travail consiste à recréer ces écrans dans l'environnement existant du dépôt : React 18 / Next.js App Router, composants clients 'use client', Tailwind avec les jetons HSL de src/app/globals.css, i18n via useLocale() de src/lib/i18n.tsx, données via src/lib/supabaseClient.ts.

Les prototypes utilisent des styles inline et des polices chargées depuis Google Fonts ; dans le dépôt, il faut passer par les classes Tailwind / globals.css et next/font/google (src/lib/fonts.ts).

Fidelity#

Haute fidélité (hifi). Couleurs, tailles de police, graisses, interlignes, épaisseurs de filets et rythme vertical sont définitifs et repris des jetons réels du dépôt. À recréer au pixel avec les classes Tailwind existantes. Seules exceptions, à remplacer par des données réelles :

  • les libellés des chapitres 02 à 06 (« Le mérite de la foi », « Le devoir de transmettre », « Les premiers auditeurs », « La dictée et la lecture », « Les recensions divergentes ») sont provisoires — utiliser chapters.title_fr ;
  • les numéros SB-HD0002 … SB-HD0022 et les compteurs de hadiths sont illustratifs ;
  • les seules lignes réellement issues du dépôt (fixtures supabase/tests/fixtures/) sont : collections 10 (صحيح البخاري / Ṣaḥīḥ al-Bukhārī) et 15 (صحيح مسلم / Ṣaḥīḥ Muslim) ; livres 92 (كتاب بدء الوحي / Révélation), 56 (كتاب الجهاد / Jihad), 95 (كتاب الفتن / Épreuves) ; chapitre 104 (باب بدء الوحي / Début de la révélation) ; hadith SB-HD0001 et sa chaîne (al-Ḥumaydī → Sufyān → Yaḥyā ibn Saʿīd → ʿUmar ibn al-Khaṭṭāb).

Screens / Views#

A. Atelier de lecture — bureau, français, thème clair (option 1a)#

Route cible : /[locale]/corpus/[collectionId]/books/[bookId] (remplace la page actuelle). Objet : lire un livre entier chapitre par chapitre sans perdre sa position, avec la chaîne de transmission lisible à côté du texte.

Layout

Colonne pleine hauteur, hors <main> actuel (l'atelier prend la hauteur de la fenêtre ; le max-w-6xl px-5 py-12 du layout ne doit pas s'appliquer à cette route — prévoir un layout de route dédié, ou un <main> sans padding pour ce segment).

┌ barre d'atelier (hauteur 51px, border-bottom 1px, bg card) ────────────────┐
│ logo ISNAD · fil d'Ariane · [Français|العربية] · thème                     │
├──────────────┬───────────────────────────────┬──────────────────────────────┤
│ rail gauche  │ colonne de lecture            │ panneau isnād                │
│ 312px fixe   │ minmax(0,1fr)                 │ 332px fixe                   │
│ overflow     │ en-tête collant + zone        │ overflow-y auto              │
│ interne      │ défilante                     │                              │
└──────────────┴───────────────────────────────┴──────────────────────────────┘
  • Grille : grid-template-columns: 312px minmax(0,1fr) 332px, hauteur = 100dvh - 51px.
  • Séparations : border-inline-end / border-inline-start 1px hsl(var(--border)) (propriétés logiques obligatoires — le rail passe à droite en RTL sans code conditionnel).
  • Les trois colonnes défilent indépendamment (min-height:0 sur chaque piste, sinon le overflow-y:auto ne fonctionne pas dans une grille).

Rail gauche (312px)

Trois blocs empilés en flex-direction:column :

  1. En-tête de livre — padding 20px 20px 16px, border-bottom 1px hsl(var(--border) / .7) :

    • kicker LIVRE 1 SUR 97 : Inter 600, 10px, letter-spacing:.18em, hsl(var(--primary)) ;
    • titre Révélation : Newsreader 400, 22px, line-height:1.2, marge haute 10px ;
    • méta 6 chapitres · 22 hadiths · 18 identifiés : Inter 400, 12px, hsl(var(--muted-foreground)), chiffres en tabular-nums ;
    • champ de filtre : border 1px hsl(var(--border)), radius 4px, padding 7px 10px, fond hsl(var(--background)), icône loupe Lucide 13px, placeholder « Filtrer les chapitres » (12,5px). Filtre en clair sur le titre français ET sur title_ar (un chercheur tape en arabe).
  2. Table des matières — flex:1; overflow-y:auto, padding 12px 12px 16px :

    • kicker TABLE DES MATIÈRES : Inter 600, 10px, letter-spacing:.16em, hsl(var(--muted-foreground)) ;
    • ligne de chapitre : display:flex; gap:10px; border-radius:4px; padding:10px 12px ; numéro Inter 400 11px tabular-nums hsl(var(--muted-foreground)), titre Inter 400 13,5px, méta (6 hadiths) Inter 400 10,5px hsl(var(--muted-foreground)) ;
    • ligne active : fond hsl(var(--accent) / .7), titre Inter 500 hsl(var(--accent-foreground)), méta suffixée « · en lecture », et une barre verticale position:absolute; inset-block:8px; inset-inline-start:0; width:2px; border-radius:999px; background:hsl(var(--primary)) ;
    • sous-liste du chapitre actif uniquement : margin-inline-start:30px, border-inline-start:1px solid hsl(var(--primary) / .35), padding-inline-start:12px ; chaque entrée Hadith n · titre court Inter 400 12,5px, padding:5px 0 ; le hadith en cours de lecture en hsl(var(--primary)), les autres en hsl(var(--muted-foreground)).
  3. Réglages de lecture — border-top 1px, padding 16px 20px 18px, fond hsl(var(--muted) / .5) :

    • kicker RÉGLAGES DE LECTURE (même style que ci-dessus, couleur primary) ;
    • libellé « Police du texte source » Inter 400 11px, puis 3 boutons en flex; gap:6px, chacun flex:1, border 1px, radius 4px, padding:8px 6px, centré : échantillon « أبجد » à 19px dans la police proposée + libellé 9,5px ; sélection = border-color:hsl(var(--primary)) + fond hsl(var(--accent) / .8) ;
    • libellé « Taille » + 3 boutons Petit | Moyen | Grand (Inter 12px, 500 si actif, padding:6px).

Colonne de lecture

  • En-tête collant (position:sticky; top:0; z-index:3), fond hsl(var(--background) / .94) + backdrop-filter: blur(8px), border-bottom 1px :
    • ligne padding:11px 32px 10px : à gauche CH. 01 (Inter 11px tabular-nums primary) + titre du chapitre courant (Newsreader 15px, text-overflow:ellipsis) ; à droite 42 % du livre (Inter 11px tabular-nums muted) ;
    • barre de progression : piste 2px hsl(var(--border) / .6), remplissage hsl(var(--primary)), transition: width .12s linear.
  • Zone défilante : overflow-y:auto, padding: 64px 32px 680px (la marge basse donne au dernier chapitre la place d'atteindre le seuil de détection — voir « Scroll-spy »), contenu centré max-width:684px.
  • En-tête de livre : kicker ṢAḤĪḤ AL-BUKHĀRĪ · LIVRE 1, h1 Newsreader 400 40px/1.1, chapeau Inter 15px/1.7 muted, border-bottom 1px, padding-bottom:28px.
  • Section de chapitre — une par chapitre, padding-top:44px (56px pour les suivantes), attributs data-ch, data-num, data-title (le scroll-spy les lit) :
    • titre : <h2> Newsreader 400 26px/1.25, précédé du numéro (Inter 13px tabular-nums primary, margin-inline-end:10px) ; à droite le compteur « 6 hadiths » (Inter 11,5px muted) ; border-bottom 1px, padding-bottom:12px ;
    • ligne « Titre source : باب بدء الوحي » — Inter 12px muted + Amiri 15px dir="rtl" à 75 % d'opacité. C'est la seule place de l'arabe côté interface française (voir « Règle monolingue »).
  • Fiche de hadith dépliée : border 1px hsl(var(--border)), radius 4px, fond hsl(var(--card)), overflow:hidden :
    • barre de titre : fond hsl(var(--muted) / .5), border-bottom 1px, padding:12px 20px ; « Hadith 1 » Newsreader 15px tabular-nums + clé SB-HD0001 en mono 10,5px muted ; à droite deux étiquettes : « 4 maillons identifiés » (bordure hsl(var(--primary) / .45), texte hsl(var(--accent-foreground)), 10px 500) et « Traduction FR à venir » (bordure tiretée hsl(var(--border)), texte muted) ;
    • corps : kicker TEXTE SOURCE · ARABE (Inter 600 9,5px letter-spacing:.16em muted), puis le matn dir="rtl", text-align:justify, police et taille pilotées par les réglages (défaut Amiri 21px / line-height:2.05) ;
    • pied : border-top 1px, fond hsl(var(--background) / .7), kicker CHAÎNE DE TRANSMISSION · DU PLUS ANCIEN AU RECUEIL, puis la chaîne horizontale (ci-dessous).
  • Fiche repliée (divulgation progressive) : ligne display:flex; justify-content:space-between, padding:15px 4px, border-bottom 1px hsl(var(--border) / .7) ; « Hadith 2 » Newsreader 14px + clé mono 10,5px ; à droite « 3 maillons · 2 identifiés », trois pastilles d'état de 7px, et l'action « Déplier » en hsl(var(--primary)).

Visualisation de la chaîne (horizontale, dans la fiche)

display:flex, un flex:1 par maillon, dernier élément flex:none; width:112px (le recueil). Chaque maillon :

  • pastille 9px (border-radius:999px) + filet horizontal 1px hsl(var(--border)) vers le maillon suivant ;
  • nom translittéré Newsreader 14px (lien vers /[locale]/narrators/[id]) ;
  • méta Inter 10,5px muted : Gén. 5 · thiqa · ʿanʿana — la formule de transmission ambiguë (ʿanʿana) est colorée hsl(41 56% 36%), les formules explicites (ḥaddathanā) restent muted.

Ordre : chronologique, du Compagnon vers le recueil (inverse de l'ordre position en base, qui part du compilateur). En RTL, l'ordre visuel se renverse naturellement — ne pas re-trier en JS.

Couleurs de pastille : Compagnon hsl(41 56% 46%) (or) · narrateur identifié et gradé hsl(var(--primary)) · mention non résolue = cercle 7–9px avec border: 1px dashed hsl(var(--muted-foreground)) et fond transparent · recueil = carré border-radius:2px hsl(var(--foreground)).

Panneau isnād (332px)

Version verticale détaillée du même isnād, padding:22px, fond hsl(var(--card) / .55) :

  • ouvert par défaut sur grand écran (décision du propriétaire du produit) ; l'état replié/ouvert est mémorisé dans localStorage comme les réglages de lecture ;
  • kicker ISNĀD · HADITH 1 + action « Masquer » (Inter 11px muted) ;
  • grille grid-template-columns: 22px 1fr : colonne de gauche = pastille 11px + filet vertical 1px hsl(var(--border)) en flex:1 (le connecteur se dessine tout seul, aucun SVG) ;
  • par maillon : nom translittéré Newsreader 16px (lien), nom arabe Amiri 15px à 55 % d'opacité, puis des étiquettes 10px 500 (thiqa · gén. 5, ʿanʿana) ;
  • sous une étiquette ʿanʿana, une note Inter 11px/1.6 muted reprenant le texte déjà présent dans src/lib/i18n-data.ts (entrée « ʿanʿana » : formule grammaticalement ambiguë, n'atteste pas explicitement une audition directe) — ne pas réécrire ce texte, le lire depuis t() ;
  • bas de panneau : border-top 1px puis une légende des trois états de pastille.

B. Même atelier en arabe, thème sombre, RTL (option 1b)#

Route : /ar/corpus/.... dir="rtl" sur <html> (déjà en place dans [locale]/layout.tsx).

Différences structurelles (pas seulement des libellés traduits) :

  • le rail passe à droite, la progression se remplit de droite à gauche : obtenu gratuitement si tout le positionnement utilise inset-inline-start/end, border-inline-*, margin-inline-* ;
  • chiffres indo-arabes partout : ٧ أبواب · ٢٢ حديثًا, الكتاب ١ من ٩٧ — Intl.NumberFormat('ar') (le dépôt fait déjà toLocaleString(locale === 'ar' ? 'ar' : 'fr') dans corpus/page.tsx) ;
  • vocabulaire des ṭabaqāt : الطبقة ١ · صحابي, ثقة, عنعنة, المصنَّف ;
  • titres de l'interface en Amiri (font-serif-ar), textes courants en Noto Sans Arabic (font-sans-ar) ; aucun mot latin sauf la clé documentaire SB-HD0001, qui reste en mono ;
  • les 3 polices proposées sont des naskh : Amiri / Noto Sans Arabic / Scheherazade New — pas des équivalents latins ;
  • couleurs = bloc .dark de globals.css (voir « Design Tokens »).

C. Mobile, français (option 1c)#

Cible 390 × 844. Le rail ne peut pas rester affiché :

  • barre haute collante border-bottom 1px, fond hsl(var(--card)) : bouton menu (Lucide, 30×30, border 1px, radius 6px, icône primary) + bloc deux lignes (CH. 1 · RÉVÉLATION Inter 10px primary / titre Newsreader 15px tronqué) + bouton Aa (34×30, bordure hsl(var(--primary) / .5), texte hsl(var(--accent-foreground))) qui ouvre les réglages ; dessous, la même barre de progression de 2px ;
  • corps : « Hadith 1 » + clé, kicker TEXTE SOURCE · ARABE, matn Amiri 19px/2 justifié, puis la chaîne verticale (pastille 9px + nom Newsreader 14px + méta 10,5px) ;
  • feuille de table des matières rappelable : border-radius:16px 16px 0 0, border-top 1px, box-shadow: 0 -18px 40px -30px hsl(var(--surface-shadow)), poignée 40×4px radius 999px, kicker CHAPITRES · LIVRE 1, lignes de 9px de padding vertical ; la ligne active reprend exactement la mise en surbrillance du rail (border-inline-start:2px solid hsl(var(--primary)), fond hsl(var(--accent) / .55), Inter 500 13,5px) et affiche 1/6 ;
  • toutes les cibles tactiles ≥ 44px de haut.

Interactions & Behavior#

Scroll-spy (le cœur de la demande)#

Le conteneur défilant est la colonne de lecture, pas la fenêtre (overflow-y:auto).

const SPY_OFFSET = 140;          // px sous le haut du conteneur
const sections = Array.from(el.querySelectorAll('[data-ch]')) as HTMLElement[];
const max = el.scrollHeight - el.clientHeight;
const pos = Math.min(el.scrollTop, max);
let active = 0;
sections.forEach((s, i) => { if (s.offsetTop - SPY_OFFSET <= pos) active = i; });
if (pos >= max - 2) active = sections.length - 1;   // verrou de fin
const ratio = max > 0 ? Math.min(1, Math.max(0, pos / max)) : 0;

Points durs constatés pendant le prototypage — ne pas les réintroduire :

  1. mesurer sur offsetTop (stable) et non sur getBoundingClientRect().top au montage : avant la mise en page, tous les rectangles valent 0 et c'est le dernier chapitre qui gagne. Première mesure dans un double requestAnimationFrame (ou IntersectionObserver), et sortie immédiate si !el.clientHeight ;
  2. le ratio et le verrou de fin doivent utiliser le même scrollHeight - clientHeight ; tout autre dénominateur (l'étendue de la dernière section, par exemple) déclenche le verrou trop tôt et rend les chapitres intermédiaires inatteignables ;
  3. il faut assez de marge basse (padding-bottom ≈ une hauteur de fenêtre) pour que le dernier chapitre franchisse le seuil en défilant, et non seulement par le verrou de fin.

Écouteur { passive: true }, setState seulement si l'index change ou si le ratio bouge de plus de 0,5 %, retrait de l'écouteur au démontage. Un clic sur une entrée de la table des matières défile vers la section (el.scrollTo({ top: section.offsetTop - SPY_OFFSET, behavior: 'smooth' }) — ne pas utiliser scrollIntoView, et respecter prefers-reduced-motion).

Réglages de lecture#

  • Police du texte source arabe : 3 valeurs amiri | noto | scheherazade → appliquées au matn, aux titres source et aux noms arabes du panneau isnād.
  • Police du texte français : 3 valeurs newsreader | garamond | inter → appliquées à la prose de la colonne de lecture (titres de livre et de chapitre, chapeaux, paragraphes), pas à l'interface (rail, en-tête collant, étiquettes, méta restent en Inter). Registre voulu : liseuse, moderne et minimaliste — Newsreader (défaut, celui de l'app), EB Garamond (lecture longue), Inter (écran). Ajouter EB Garamond à src/lib/fonts.ts (next/font/google, subset latin, variable --font-reading-latin). Implémentation : une seule déclaration font-family sur le conteneur de prose (max-width:684px) et les titres/paragraphes qui héritent — ne pas figer de famille dans leur raccourci font:.
  • Taille : petit | moyen | grand = 18 / 21 / 25 px, interlignes 1.95 / 2.05 / 2.1.
  • Persistance en localStorage sur le modèle de src/lib/theme.tsx (clés distinctes, p. ex. isnad.reading.font / isnad.reading.size), lues dans un script d'initialisation comme src/lib/theme-script.ts pour éviter le flash au premier rendu.
  • Implémentation : poser un attribut data-reading-font / data-reading-size sur le conteneur et laisser globals.css résoudre --font-reading / --size-reading — pas de style inline par élément.
  • Scheherazade New doit être ajouté à src/lib/fonts.ts (next/font/google, subset arabic, variable --font-naskh-alt).

Règle monolingue#

Règle à appliquer partout dans le segment corpus :

L'interface est dans une seule langue, celle de la route. L'autre écriture n'apparaît que comme citation de source explicitement étiquetée.

Concrètement, en fr :

  • ne plus laisser title_ar gagner sur title_fr — le code actuel fait l'inverse (dans corpus/[collectionId]/page.tsx, la classe passe à text-right font-serif-ar dès que book.title_ar existe, et la valeur affichée retombe sur title_ar avant title_fr) ; attendu : en fr, afficher title_fr ; si title_fr est nul, laisser le titre arabe tel quel (décision du propriétaire du produit) — en dir="rtl", en Amiri, précédé de la mention « Titre source », jamais remplacé par un numéro ;
  • la carte de recueil n'empile plus les deux titres (retirer la branche collection.name_ar && locale !== 'ar' de corpus/page.tsx) ; l'étagère OpenITI expose le titre translittéré, l'arabe passe en ligne « titre source » ;
  • le matn arabe reste affiché (la traduction n'est pas en base) mais sous un kicker TEXTE SOURCE · ARABE et accompagné de l'étiquette « Traduction FR à venir » ;
  • les noms de narrateurs utilisent full_name_fr (translittération) ; l'arabe figure en regard, à 55 % d'opacité, dans le panneau isnād seulement.

En ar : symétrique, aucun mot latin hors clés documentaires.

États#

  • Chargement : squelettes de lignes dans le rail (6 lignes de 44px) et une fiche de hadith fantôme ; ne jamais afficher un livre vide pendant le chargement.
  • Erreur : conserver le comportement actuel (reportLoadError + message t('loadError') + bouton « Réessayer » qui incrémente un reloadNonce) — une panne ne doit pas se présenter comme un livre vide.
  • Vide : t('corpusEmpty').
  • Survol : lignes de la table des matières → fond hsl(var(--muted) / .55) (classe .surface-interactive existante) ; liens de narrateur → soulignement.
  • Focus clavier : anneau existant :focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px } — ne pas le redéfinir. La table des matières est un <nav> avec une liste de liens ; l'ordre de tabulation suit l'ordre de lecture.
  • prefers-reduced-motion : supprimer le défilement animé et la transition de la barre de progression (globals.css a déjà un bloc de ce type pour .book-spine).

Responsive#

  • ≥ 1280px : trois colonnes.
  • 1024–1279px : panneau isnād replié en tiroir (bouton « Isnād » dans l'en-tête collant).
  • 768–1023px : rail replié en tiroir, colonne de lecture pleine largeur.
  • < 768px : disposition mobile (écran C).

State Management#

Composant client unique (ReadingWorkspace), état local :

ÉtatTypeSource / déclencheur
activeChapterIndexnumberscroll-spy sur la colonne de lecture
scrollRationumber 0→1même écouteur ; alimente la barre et « n % du livre »
expandedHadithIdsSet<number>clic sur « Déplier » (divulgation progressive)
selectedHadithId`numbernull`
tocFilterstringchamp de filtre (titre FR et title_ar)
readingFont`'amiri''noto'
readingSize`'petit''moyen'
railOpen / isnadOpenbooleantiroirs sous 1280px

Données#

Une seule requête suffit pour la page (elle existe déjà dans corpus/[collectionId]/books/[bookId]/page.tsx) : chapters filtrés par book_id, ordonnés par chapter_number, avec les hadiths imbriqués (id, chapter_id, hadith_number, hadith_number_raw, source_key, text_ar, text_fr).

À compléter pour l'atelier :

  • books : title_fr, title_ar, source_sequence_number, et le total de livres du recueil (compteur « LIVRE 1 SUR 97 ») ;
  • chaînes : hadith_chains(id, chain_number, hadith_chain_nodes(id, position, narrator_id, mention_ar, transmission_term_ar)) — déjà utilisé par la fiche hadith ;
  • grades et générations des narrateurs cités : narrators(id, full_name_fr, full_name_ar, grade_tier, generation_rank) (voir src/lib/grade-tiers.ts, src/lib/tabaqa.ts) ;
  • la formule de transmission (ḥaddathanā / ʿanʿana) vient de transmission_term_ar du maillon ; les libellés explicatifs existent dans src/lib/i18n-data.ts.

Charger les chaînes des hadiths repliés à la demande (au dépliage) pour ne pas alourdir un livre de plusieurs centaines de hadiths ; ne charger que la chaîne du hadith sélectionné pour le panneau de droite.


Design Tokens#

Tous repris de src/app/globals.css (HSL brut, notation Tailwind hsl(var(--x) / <alpha-value>)) — ne pas coder de hex en dur.

Clair (:root)#

JetonValeurEmploi dans la refonte
--background39 39% 94%fond de la colonne de lecture
--foreground36 8% 10%texte courant, pastille « recueil »
--card39 35% 97%barre d'atelier, fiche de hadith, feuille mobile
--border40 28% 80%tous les filets, connecteurs, pistes
--muted39 24% 90%bloc réglages, barre de titre de fiche
--muted-foreground31 7% 45%méta, kickers secondaires
--primary146 15% 38%kickers, barre active, pastilles de narrateur, progression
--accent143 17% 86%fond de la ligne active, étiquettes sélectionnées
--accent-foreground146 18% 27%texte sur fond accent
--ring146 18% 38%anneau de focus
--surface-shadow170 30% 10%ombre de la feuille mobile

Sombre (.dark)#

--background 150 8% 8% · --foreground 39 24% 90% · --card 145 7% 11% · --border 45 9% 22% · --muted 145 6% 15% · --muted-foreground 38 8% 65% · --primary 162 34% 58% · --accent 169 19% 15% · --accent-foreground 158 16% 90%.

Couleur ajoutée#

Une seule : l'or des Compagnons et des formules de transmission ambiguës — hsl(41 56% 46%) en clair, hsl(41 56% 56%) en sombre, texte hsl(41 56% 30%) / hsl(41 56% 72%). À déclarer comme jeton (--sahabi / --sahabi-foreground) dans globals.css, pas en dur.

Typographie#

RôlePoliceVariableTailles utilisées
Titres latinsNewsreader--font-serif40 / 26 / 22 / 16 / 15 / 14 px
Interface latineInter--font-sans15 / 13,5 / 12,5 / 12 / 11,5 / 11 / 10,5 / 10 / 9,5 px
Titres arabesAmiri--font-serif-ar40 / 24 / 21 / 19 / 17 / 15 px
Interface arabeNoto Sans Arabic--font-sans-ar12,5 / 11,5 / 11 / 10,5 px
Naskh alternatifScheherazade Newà ajoutertexte source au choix du lecteur
Clés documentairesmono système—10,5 px, letter-spacing:.04em

Kickers : Inter 600, 9,5–10px, letter-spacing:.16em à .18em, en majuscules. Chiffres : font-variant-numeric: tabular-nums sur tous les compteurs, numéros de chapitre et pourcentages. Interlignes du matn : 1.95 / 2.05 / 2.1 selon la taille choisie (le .arabic-reading actuel est à 2.05).

Espacement et rayons#

Padding de rail 20px · lignes de table des matières 10px 12px · fiche 26px 28px · colonne de lecture 64px 32px 680px · écart entre sections de chapitre 56px · rayons : 4px (cartes, boutons, champs), 6px (boutons mobiles), 16px (feuille), 999px (pastilles, pilules). Ombres : uniquement celles de tailwind.config.ts (shadow-card, shadow-elevated) ; l'atelier n'en utilise qu'une, celle de la feuille mobile.


Assets#

  • Marque ISNAD : composant existant src/components/Logo.tsx (LogoMark + Logo), inchangé.
  • Icônes : Lucide (loupe, menu, soleil/lune) — le dépôt les dessine actuellement en SVG inline dans ThemeToggle.tsx / NavBar.tsx ; garder la même approche ou passer à lucide-react, mais pas les deux.
  • Aucune image : la refonte n'ajoute aucun visuel. Les connecteurs de chaîne sont des div de 1px, pas des SVG.
  • Polices : Inter, Newsreader, Amiri, Noto Sans Arabic déjà chargées par src/lib/fonts.ts ; ajouter Scheherazade New.

Files#

Dans ce paquet :

FichierContenu
Corpus - etat actuel.dc.htmlreconstitution 1:1 des 4 écrans actuels (/fr/corpus, /fr/corpus/10, …/books/92, fiche hadith en thème sombre), avec sous chaque écran le constat de ce qui bloque
Corpus - refonte lecture.dc.htmlla refonte : 1a bureau FR clair interactif (scroll-spy, 3 polices, 3 tailles réellement fonctionnels), 1b bureau AR sombre RTL, 1c mobile FR
support.jsruntime des deux fichiers de design (ouvrir les .dc.html directement dans un navigateur)

Dans le dépôt isnad-app-fr, fichiers concernés :

FichierAction
src/app/[locale]/corpus/[collectionId]/books/[bookId]/page.tsxremplacé par l'atelier de lecture
src/app/[locale]/corpus/[collectionId]/page.tsxrègle monolingue sur les titres de livres ; rail réutilisable
src/app/[locale]/corpus/page.tsxrègle monolingue sur les cartes de recueil et l'étagère OpenITI
src/app/[locale]/corpus/[collectionId]/books/[bookId]/hadiths/[hadithId]/page.tsxla fiche hadith reprend la chaîne visualisée (aujourd'hui une ligne à flèches)
src/app/globals.cssjetons --sahabi, --font-reading, --size-reading ; classes de l'atelier
src/lib/fonts.tsajout de Scheherazade New
src/lib/i18n-data.tslibellés : réglages de lecture, polices, tailles, « titre source », « n % du livre », « Déplier », légende des pastilles
src/lib/theme.tsx / src/lib/theme-script.tsmodèle de persistance à dupliquer pour les réglages de lecture
tailwind.config.tsinchangé sauf si les nouveaux jetons doivent être exposés en classes

Décisions arrêtées (propriétaire du produit, 2026-09-13)#

  1. Chapitres sans title_fr : laisser le titre arabe tel quel, étiqueté « Titre source ». Pas de substitution par un numéro documentaire, pas de libellé inventé.
  2. Panneau isnād : ouvert par défaut sur grand écran (≥ 1280px), repliable et état mémorisé.
  3. Choix de police pour le texte français : oui, trois faces, registre liseuse moderne et minimaliste (Newsreader / EB Garamond / Inter). Voir « Réglages de lecture ».