
Céline Rochat
Rédactrice en chef de REISO depuis novembre 2020, Master ès Lettres, journaliste RP.
The single source of truth for REISO's visual identity: colour, typography, spacing, components and rules. Use these tokens and patterns for anything built for the site so every page stays consistent.
Four rules guide every visual decision.
base.css; no hard-coded values.The wordmark is the primary brand asset. It ships in two files - the teal mark and its white inverse - and in one set of sizes across the header and footer.
/logo.svg on white; on the dark navy banner use the white inverse /logo-inverse.svg. Never place it on busy imagery.logo-inverse.svg is the same artwork in pure white (#FFFFFF); both files share the same artboard, so they render at identical dimensions. --primary-light is the soft tint of the family.
A single teal family plus neutral ink. Orange has been fully removed from the scheme.
Two faces: Inter for the interface and Playfair Display as an editorial accent. Both are self-hosted in static/fonts/.
h2) carries a short teal rule under its text (44×3 px, --primary) - the same underline introduced on article prose - while the heading face itself stays sans-serif. Regular text - cards, panels, sidebar widgets, notices and empty states - uses one size everywhere, the --fs-text token (14 px); long-form reading prose is the deliberate exception (article and document prose, 16–17 px).
Punctuation. Never use an em dash (U+2014); use a hyphen (-) for a break or an aside. En dashes (U+2013) stay where they are correct, for numeric ranges such as 16–17 px.
Spacing grows in 4 px steps. Prefer a token over an arbitrary value.
Surfaces are softly rounded. Every chip, tag and badge uses the fully-rounded pill radius, matching the “À la une” badge.
Three soft shadows lift surfaces off the grey canvas.
One white sheet: the content region is 1280px wide (--max-width − 2 × --gutter). One gutter governs the page - header, footer, content columns and sidebar all stop on the same margin at every width. Banners (hero, page-head, detail hero) span that region and give their own content the same gutter as an inner margin, so every element inside a banner shares one consistent left/right margin.
.panel) and grid cards carry a 1px --line frame + --shadow-sm; a 3px --accent seam sits under every banner/heroEvery list (Emploi, Agenda, Formation, Fil de l'actu, Parutions, Sélections, Articles) shares one shell: the content section sits on the one page --gutter, a visible .portal-results-title h2 leads the page above the filter module, and the filter module (.portal-filter) is flat and spans the full content width with no frame - the full-width search and the “Filtres avancés” toggle share one row (the toggle to the right of the search), and the criteria slide open in a collapsible panel (.portal-advanced) whose applied-criteria row reserves its height so nothing jumps when a filter is added.
Reference renderings of the shared UI. Reuse the real classes in components.css and base.css rather than copying styles.
The global header carries five high-scent primary items - Lire (the only dropdown, grouping Revue - Nos articles, Le fil de l'actu - and Publications - Liste des parutions, Sélections des bibliothèques), Agenda, Emploi, Formation and À propos - plus a filled S'abonner CTA (.btn-cta), styled from the teal tokens, and the account/search icons. The utility bar above holds the secondary links (Publier, Nous contacter). The current page’s label is marked by a 3px teal rule; the same sliding rule (.nav-rule, driven by assets/js/common.js) glides between the top-level items - and between the children inside the Lire dropdown - as the pointer or keyboard focus moves, then returns to the active item, and switches off under prefers-reduced-motion. The search icon opens a full-width panel (.site-search) anchored under the sticky bar: it lazily fetches the build-time index /recherche-index.json - every standalone page plus every list item (articles, offres d'emploi, formations, agenda, fil de l'actu, parutions, sélections) - filters case- and accent-insensitively, highlights matches (mark) and tags each result with a fully-rounded type pill (.site-search-type); it closes on Escape or an outside click. The account icon opens a simulated session menu (.user-menu, seeded by [params.session] in hugo.toml) showing Céline Rochat with two stub actions - Modifier mon profil and Se déconnecter - that only surface a “fonction de démonstration” notice (there is no authentication on the static site). On mobile the account icon stays in the narrow header beside the burger, and the S'abonner CTA stays in the bar down to the small-phone breakpoint (480px); on small phones the CTA joins the search icon in the drawer (whose Rechercher opens the same search overlay); the drawer's close control (.mp-close) is parked on the exact spot the header burger (.menu-toggle) occupies, sharing the same three-bar icon (.menu-bars) that folds into an X as the drawer eases in (both instant under prefers-reduced-motion). Only items with children render a caret - .nav-expand in the desktop nav and .mp-expand in the drawer - and their submenu starts folded, opened from that caret; the parent link itself still navigates, so a top-level item is never both a heading and a page. The current page carries a short 3 px teal rule under its label - in the desktop nav and inside the mobile drawer - and a top-level item stays marked when one of its children is the current page. Dropdown links and footer links are kept on one line (white-space: nowrap) on larger screens - footer links wrap on small phones (≤480px) so the two columns never overflow the viewport - and the dropdown sizes to its widest entry. Dropdowns group related links under .dropdown-group headings. The active section is marked with aria-current="page" on the parent - its label takes the --primary colour and a --primary 3px rule reusing the h2 heading rule - and .dropdown li.active on the child. On mobile the bar collapses to a labelled Menu button; the drawer repeats the same taxonomy and pins the S'abonner CTA. Menus are defined in hugo.toml ([[menus.main]]); the footer columns and legal row live in data/footer.yaml and both mirror this one taxonomy.
One card element drives every list on the site - editorial pages and the filterable portals alike: layouts/partials/card.html, exposed as the card shortcode. The whole card is a single <a>, so there is no trailing “read more” call to action. Parameters - href, title, meta (allows inline HTML), excerpt, image, mediaChip (a single pill rendered inside the media, top-right - e.g. the frontpage theme article count), feature (a single teal “dossier annuel” pill), chip (a single grey tag pill), chips (a |-separated list of further grey tag pills - for facets such as work rate, contract, region and deadline) and attrs (a |-separated key=value list rendered as HTML attributes for the portal filters, e.g. data-region, data-type, data-rate). Dossier and tag pills render after the excerpt (never on the image): the dossier in --primary, tags in grey; use mediaChip when a pill belongs on the image (it renders as a solid teal badge, top-right).
A second, dossier-specific card - the cover with an article-count pill, the credit caption directly beneath it, plus the year, motto and description - is shared by the dossiers annuels timeline and the article-detail sidebar. It is rendered by layouts/partials/dossier-card.html and its styles (.dossier-node*, .dossier-node-cover) live in components.css. The motto anchor's stretched ::after makes the whole card clickable while the credit keeps its own link (never an <a> inside an <a>). The credit overlay itself (.photo-caption) is no longer on the image: it is a small --text-muted line on the white sheet directly under the hero or cover, with the photographer linked. That is 10.5:1 on white - nothing overlayering the photography.
L'association Connect! développe de nouvelles approches pour faire face à l'isolement des personnes âgées.
Questionner, repousser et dépasser les limites du travail social avec et pour les personnes en situation de handicap.
The .card-grid--banner variant lays the cards out one per row as full-width horizontal banners - meta, title and excerpt on the left, the facet pills on the right - deliberately different from the multi-column grids above. It is card-source agnostic (it styles any .card, whether authored by the shortcode or rendered from list data) and is used by the frontpage job offers and upcoming formations.
An icon-led summary card - a round teal .info-card-icon beside an h3, then .info-card-text - for short grouped explanations such as the S'abonner “Vos avantages” / “Informations” groups and the article-type cards on Proposer un article. Lay them out in a .info-card-grid (auto-fit minmax(240px, 1fr)); .info-card-grid--2 forces two desktop columns for longer copy and collapses to one at 768px. The default surface is --bg-soft; .info-card--tint switches it to --primary-light to mark an emphasis group.
Les références théoriques ou historiques sur lesquelles se base l'article sont citées et expliquées avec précision et concision.
Exemple de carte teintée (.info-card--tint) pour un groupe mis en avant.
A .notice sets a short passage apart from the surrounding prose - an editorial caveat or a rule to remember. It is a flex banner on a --primary-light surface with a --primary left accent bar, a round .notice-icon and a .notice-text paragraph. Because the palette allows no second accent, emphasis stays in the teal family. Used on Proposer un article for the editorial decision-finale caveat.
Exemple de bandeau d'avertissement (.notice) pour un point à retenir.
One collapsible region for the whole site: a trigger ([data-disclosure-toggle]) reveals a region ([data-disclosure-region]) by animating its grid row 0fr→1fr - the same height-agnostic technique as the filter panel - with a gentle fade/rise on the revealed content, both disabled under prefers-reduced-motion. The trigger is a bare <button>; .disclosure-toggle--link renders it as the quiet text link used in a block head, .disclosure--panel as the soft full-width panel below. Behaviour is one shared script, assets/js/disclosure.js; the region is collapsed by default and a <noscript> rule in head.html reveals it without JavaScript. Used by the frontpage themes (“Voir tous les thèmes”) and the À propos “Ancien·ne·s de la revue” panel.
Contenu révélé par le déclencheur. La hauteur s'anime en douceur, sans max-height codé en dur.
Every list page - Emploi, Agenda, Formation, Fil de l'actu, Parutions, Sélections - renders through one data-driven layout, layouts/_default/portal.html, from a data/<name>.yaml file plus a portal block in front matter. The filter panel is a single component, layouts/partials/portal/filter.html, that switches on each criterion's type into one portal/filter/<type>.html partial (select, chips, multiselect, slider, year, dates, check): adding a list is front matter + data, adding a control type is one partial. Option lists are the union of the items' facets, so a vocabulary is written once.
The .portal-results-title h2 leads the page - plain text like “26 offres d’emploi”, composed from the page’s front-matter countLabel with the number in [data-portal-count]. Below it the full-width label-less instant search (an icon input.filter-input with data-portal-search) shares one row with the .portal-toggle “Filtres avancés” button - carrying a live count badge of the active filters - so the toggle sits to the right of the search. The criteria live in a collapsible .portal-advanced panel that slides open on request (grid-template-rows 0fr→1fr, disabled under prefers-reduced-motion); once settled, its overflow is released so an open dropdown panel can extend past the wrapper instead of being clipped. Range sliders - the slider and year criteria - always render at the end of the panel, after every other criterion, so the panel closes on its sliders. Its first row, .portal-applied, keeps its height even when empty: chosen criteria render there as removable .filter-active-chip pills (fully rounded, --primary-light) on the left, with the .filter-reset “Annuler” action on the right, revealed once anything filters. Only where a page offers more than one view - or a sort - the segmented .filter-views / .filter-view selector sits in the search row, pinned right (full-width on mobile); the Articles archive uses it for Grille/Explorer and the communauté list for sorting by Nom or Plus actifs. There is no “Rechercher” button and no list view: every list offers the grid. Typing filters the cards live and highlights every match inside them (mark.search-mark, the same token-based highlight as the dropdown options; matching is case- and accent-insensitive). Year-based lists add the sticky .portal-timeline rail (years and counts derived from the cards, click to jump). Results are the shared .card-grid--3 (3 columns desktop → 2 tablet → 1 mobile) of the standard .card; a list can opt into a different card partial through portal/results.html's card param - the community list uses a person card (partials/person-card.html, .card--person) that leads with the portrait (an initials monogram when no photo is on file), then the name, the function, the total article count and the type pills. The frontpage keeps its narrower 2-column .card-grid for articles, while its job offers and upcoming formations use the full-width .card-grid--banner. A portal can override its results grid with grid: "banner" and group its results under month H2s with groupBy: "month" (.portal-month, derived from each item's facets.date; the engine hides a band whose cards are all filtered out) - the Agenda uses both. Criteria types: single-select (.a-dropdown) and multiselect (.a-dropdown) dropdowns - every panel opens with a freetext search row (portal/filter/search-field.html, wired by PortalUI.wireDropdownSearch) that filters its own options case- and accent-insensitively, highlights the match with mark.a-dropdown-mark, pins selected rows in a multiselect and shows a live .a-dropdown-foot option count - chips, slider (numeric, dual-handle min–max on .range-slider), year (the full-width publication-year bar, on the same range component, shared by the Articles archive and the year-filtered lists), dates and check. (chips remains available for a short vocabulary; the year-filtered lists use the year bar.) Every option entry carries a live hit-count pill (.filter-pill) - faceted, so it shows what that entry would yield under the current search and all other filters. The range slider is one shared component (PortalUI.rangeSlider + the .range-slider* / .portal-year-bar classes in components.css), used by both the slider criterion and the Articles/portal year bar. Every criterion is label-less: the control’s own text carries the meaning - the all placeholder, the chip row, the checkbox text, the de / à date prefixes - while label is emitted only as the control’s accessible name (aria-label). Criteria sit on a uniform column grid: each .filter-row is a fixed-width track grid (repeat(auto-fill, minmax(200px, 1fr))) whose dropdowns stretch to fill their track, so every control in a row is the same width - a wide control spans two tracks (.filter-group--dates) and the slider spans the whole row (.filter-group--slider), and a short row keeps that control width instead of stretching. Pages split the criteria across lines with row: N on the first criterion of a line; a criterion that omits row stays on the current line.
A card may carry a single badge pill (.card-badge-row wrapping one .chip) pinned to its own right-aligned row above the facet tags - the frontpage formation cards use it for the course length so it never wraps into the topic tags.
.a-dropdown filters, .card-grid--3 and .portal-timeline; the D3 graph (assets/js/articles-explorer.js) renders article/author/dossier/theme nodes in .article-explorer. Colours stay in the palette: articles --dark, authors --primary, dossiers --accent (the lighter teal), themes --neutral.
One reusable person card for team and committee listings: layouts/partials/person.html, exposed as the person shortcode. Parameters - image (a portrait; when omitted a monogram in the brand teal is drawn from the initials), name, description (role or short bio), tag (an optional kicker pill) and href (renders the card as a link). Lay them out in a .person-grid.

Rédactrice en chef de REISO depuis novembre 2020, Master ès Lettres, journaliste RP.
Professeure et chercheuse, Haute école de travail social, Fribourg.
The “L'histoire de REISO” timeline on À propos is a page-scoped pattern in assets/css/pages/a-propos-de-reiso.css: a .timeline ordered list with a teal spine, a milestone dot per .timeline-item, the year in a left rail (a solid teal pill for .is-latest) and each entry in a .tl-card. Items fade up on scroll via assets/js/a-propos-de-reiso.js, disabled under prefers-reduced-motion.
Every detail page (article, actualité, ressources, sélections, emploi, agenda, formation) is rendered by layouts/_default/detail.html and opts in with layout: detail. The header, sidebar and pager are driven by the page's detail: front matter; the body holds only the lead and prose. Styles live in assets/css/pages/detail.css (auto-loaded for that layout). By default the prose column keeps the shared 760px reading measure; set wide: true to drop it so the content fills the full width of the article card (the parution detail uses this).
The head is .detail-head, inset to match the body. Its first row is .detail-topline - a .detail-back link on the left and optional .detail-tags / .detail-tag pills on the right at the same height. The coverless title sits in .detail-title-row with an optional right-aligned .detail-logo - or a round .detail-portrait for a profile page, as on a person page - on the same line. The .detail-info bar lists the facts - publication date first, then any mix of text, links and (for editorial pages) a clock-prefixed read time - separated by .detail-info-sep. A page with a cover uses .detail-hero, with the back link, tags and title overlaid on the image and the info bar below. The info bar and authors are always sans-serif. The author byline (.article-authors, one .article-author row with a round photo, name and role) lays out in three columns on desktop and stacks to a single column at 768px and below; when an entry carries an href the row becomes a link (.article-author--link) with no underline, the name picking up the teal on hover - this is how the article detail leads to a person's page. A person page uses the profile header (.detail-person, opt-in via detail.personHeader): the portrait at the far left, then the name in sans-serif, with the type(s) as .detail-tag pills on the function row; each pill links back to the community list with that type already selected (/communaute/?type=…). A tag without an href falls back to a non-link pill. Editorial pages add .detail--editorial (and .article-card--editorial) to switch the title - and only the title - to the serif face.
Sidebars switch on detail.sidebar.mode: rail mounts the shared side-rail widgets, readMore a .panel + .panel-list, facts a .panel + .side-facts (plus optional contact/latest panels). An optional CTA renders as .btn-solid .detail-cta. Set affiche: true to slot the “Affiche de la semaine” card between the CTA and the mode panel (requires widgets: true; on desktop and tablet it stacks in normal flow and shares the same daily close flag as the rail, while at ≤768px it becomes the same fixed bottom bar). Prev/next navigation uses .article-pager with .pager-link.is-prev / .is-next, each carrying a .pager-dir, a .pager-title and a .pager-org line.
Every sidebar and section panel is a .panel: white surface, --radius-lg, --shadow-md, 22px 24px padding (in step with the “Affiche de la semaine” widget), with a .panel-title. Link lists use a .panel-list of .panel-item rows - hairline-separated, each opening with a .panel-item-top row holding the muted .panel-item-meta and any .panel-item-tag pills beside it, followed by a serif .panel-item-title. This is the “Lire également” pattern, applied site-wide.
.panel with a .panel-head holding only the title, a carousel of .carousel-slide items, and a .panel-foot carrying the .panel-dots indicator on the left and the .panel-nav prev/next arrows on the right. The newsfeed slide leads with a .carousel-date; the agenda card, fed by data/agenda.yaml, leads with a date · durée · lieu .carousel-meta line followed by the title and the short description. Each .carousel-slide is itself the link (the whole slide is clickable, no trailing call to action). They are assembled by the reusable side‑rail component ({{< widgets/side-rail >}}, see layouts/partials/widgets/side-rail.html) used by the homepage, the listing pages and the parution detail. On the pages that mount the rail (homepage, listing), on mobile the “fil de l'actu” card is ordered directly after the page hero (marked data-rail-hero) with CSS, while on every page the affiche becomes a fixed bottom bar spanning the full content width (viewport minus the --gutter) - whether it is mounted in the rail or standalone in a detail sidebar (affiche: true). The ad's copy and image are edited in content/affiche.md: the Markdown body is the regular copy, and on mobile it is swapped for the short mobileText only when it would exceed three lines (never clipped); the short copy is then shrunk to fit two lines on narrow screens - both by assets/js/affiche-copy.js. In the detail rail card the three cards otherwise stack in the sidebar column. The affiche card is clickable as a whole - including the mobile bottom bar, where the “Découvrir” call to action is hidden - pointing to the same destination as that call to action. The close control stays separate and only dismisses the ad.
The subscription page (S'abonner) is a page-scoped interactive pattern in assets/css/pages/s-abonner.css + assets/js/abonner.js: a .abo grid lays the steps beside a sticky .abo-summary. Each step is a .abo-step fieldset with a numbered .abo-legend. Choices are .abo-option cards built on a visually hidden radio and an .abo-option-card (teal border + --primary-light fill when :checked); formula cards show their price, and an optional .abo-badge pill marks the reduced rate. Selecting a type swaps the matching formula and field groups, and the summary mirrors the plan and price. Payment is always by invoice - the summary states that the invoice is sent automatically by e-mail - with no online payment and no payment-details step. It is a non-functional prototype; Odoo will provide the backend.
Contrast and focus are part of the brand, not an afterthought.
<h1> per page, never skipping a level. Headings needed for the outline but not the visuals use the .visually-hidden utility (e.g. the portal results section).prefers-reduced-motion. The mobile drawer locks the page behind it and returns focus to the toggle on close.base.css for every colour, radius and shadow.-), never an em dash.Source : https://reiso-website.opencode.socialcloud.ch/design-system/