Pour réunir les savoirs et les expériences en Suisse romande
Style guide

Design system

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.

Principles

Four rules guide every visual decision.

  • Teal is the brand. Dark teal is the default that carries actions and text; the brighter REISO teal survives only as a sparing decorative accent. There is no orange anywhere in the palette.
  • Calm, editorial, legible. Generous whitespace, restrained colour and clear hierarchy keep long-form reading comfortable.
  • One accent, used sparingly. Dark teal marks what matters (a link, an active filter, a kicker); the bright REISO teal is reserved for a few decorative marks and never carries text.
  • Tokens, not one-offs. Colours, radii and shadows come from the variables in base.css; no hard-coded values.

Colour

A single teal family plus neutral ink. Orange has been fully removed from the scheme.

Brand & primary

Dark teal --primary · #1F7885 The default: buttons, links, active states, borders, solid pills, icons.
REISO teal --accent · #2FB9CC Decoration only, used sparingly: the banner seam under each hero and the Explorer's feature/dossier graph nodes.
Light teal --primary-light · #EAF8FA Tinted backgrounds, soft badges, hover fills.

Ink & neutrals

Dark navy --dark · #1A3A4A Headings, dark banners, panel headers.
Body text --text · #333333 Default paragraph and body copy.
Secondary text --text-soft / --text-muted · #3F3F3F Meta, captions, less prominent copy.
Mid neutral --neutral · #8EA3AB Data-viz fills (graph nodes) and de-emphasis.
Line --line · #E8E8E8 Hairline dividers, input borders.
Line light --line-light · #F2F2F2 Hairline dividers, chip borders.
Soft background --bg-soft · #F5F5F5 Chips, inset panels, muted fills.
Page background body · #EEF0F2 The grey canvas behind the white cards.
White --white / --bg · #FFFFFF Cards, header, surfaces.

Functional

Success #1E6B2F on #E8F6EA (border #BFE3C4) Form confirmation messages.
There is no orange and no second chromatic accent. The default is --primary (dark teal #1F7885): links, kickers, dates, buttons, badges and solid surfaces that carry white text all use it. --accent (REISO teal #2FB9CC) is decoration only, used sparingly — the 3px banner seam under each hero and the Explorer's feature/dossier graph nodes — and it never carries text. --primary-light is the tint for soft backgrounds.

Typography

Two faces: Inter for the interface and Playfair Display as an editorial accent. Both are self-hosted in static/fonts/.

Playfair Display · 600 Imaginer la ville de demain
Inter · 600 / 34px Contacter REISO
Inter · 600 / 26px Les derniers articles
Inter · 600 / 19px Un outil contre la solitude des seniors
Inter · 400 / 17px Le corps de texte privilégie la lisibilité : interlignage généreux et teinte neutre, pour les articles comme pour les pages d'information.
Inter · 400 / 14px Le texte courant — cartes, panneaux et widgets de la colonne latérale — partage une seule taille sur tout le site.
Inter · 400 / 13px Par Diego Rigamonti et Jérôme Grand · 8 min de lecture
Inter · 700 · kicker À la une
Playfair Display is reserved for an editorial voice: the homepage hero headline and article titles/headings. Everything functional — navigation, buttons, meta, page headings — uses Inter. Every section heading (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).

Spacing

Spacing grows in 4 px steps. Prefer a token over an arbitrary value.

4 px
8 px
12 px
16 px
24 px
32 px
40 px
48 px
64 px

Corner radius

Surfaces are softly rounded. Every chip, tag and badge uses the fully-rounded pill radius, matching the “À la une” badge.

--radius-sm · 6px
--radius · 10px
--radius-lg · 16px
--radius-pill · 999px
circles · 50%
Pills, tags and badges: always --radius-pill. Cards and inputs: --radius. Large panels and page heads: --radius-lg while they sit inset; once a banner or the hero spans the full viewport its bottom corners are squared. Action buttons keep their own 20–24 px rounding; avatars and icon buttons are circles.

Elevation

Three soft shadows lift surfaces off the grey canvas.

--shadow-sm
--shadow-md
--shadow-lg

Layout & breakpoints

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.

--max-width1344px (1280px content + 2 × 32px gutters)
--gutter32px desktop · 24px ≤1080px · 18px ≤768px — the single horizontal gutter
--header-h104px desktop · 64px ≤880px
page shellbackground white everywhere; content region 1280px, no side padding, no section card
contrastmain content flat; sidebar widgets (.panel) and grid cards carry a 1px --line frame + --shadow-sm; a 3px --accent seam sits under every banner/hero

Breakpoints

max-width: 1080pxTablet — tighter nav and sidebars become static.
max-width: 960pxLegal footer row stacks and centres as soon as the copyright and the legal links no longer fit side by side.
max-width: 880pxUtility bar loses its slogan; header collapses to the burger menu.
max-width: 768pxMobile — footer columns, full-width utility bar, single column.
max-width: 480pxSmall phones — stacked actions.

List pages

Every 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.

Components

Reference renderings of the shared UI. Reuse the real classes in components.css and base.css rather than copying styles.

Buttons

Header & navigation

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.

Chips & pills — all fully rounded

Taux 80% Région GE Politique À la une Agenda
The “À la une” badge on the homepage uses --primary (dark teal carries white text at AA contrast) and the same pill radius. Category chips are grey; thematic chips are light-teal; solid teal is reserved for emphasis.

Cards

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.

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.

Info card

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.

Analyse · diagnostic

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.

Un privilège d'insertion

Exemple de carte teintée (.info-card--tint) pour un groupe mis en avant.

Notice — callout banner

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.

Disclosure — “Voir plus”

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.

Listing portals — search, filters & timeline

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.

Vaud
2025
2026

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.

The Explorer mode is exclusive to the Articles archive — the only page with a second view, so the only page that uses the view selector for a second view (the authors list reuses it as a sort control). It shares the same search shell, .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.

Person

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.

Céline Rochat
Rédaction en chef

Céline Rochat

Rédactrice en chef de REISO depuis novembre 2020, Master ès Lettres, journaliste RP.

Annamaria Colombo

Professeure et chercheuse, Haute école de travail social, Fribourg.

Timeline

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.

Detail pages — one parametrised layout

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.

Imaginer la ville de demain avec les jeunes
03.08.2026 8 min

Panel — the one sidebar/section card

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.

The side‑rail widgets (“Le fil de l'actu”, “Les prochains rendez-vous”) are the same .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 moves directly after the page hero (marked data-rail-hero), 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.

Form fields

Abonnement — sélecteur de formule

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.

Focus state

Élément au clavier

Accessibility

Contrast and focus are part of the brand, not an afterthought.

  • Teal text on white. Use --primary (#1F7885) for text and links — it meets AA on white at 5.0:1.
  • White on teal. A teal surface that carries white text uses --primary (#1F7885) — the AA-safe pair (5.0:1). --accent (#2FB9CC) is a decorative mark only (the 3px banner seam and the Explorer's feature nodes) and never carries text.
  • Focus visible. Interactive elements keep a 2 px --primary outline with a 2 px offset.
  • Never colour alone. Pair colour with text or an icon to convey state.
  • Continuous headings. One <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).
  • Respect motion. Carousels pause on hover and on focus, stay out of the tab order when off-screen, and never auto-advance under prefers-reduced-motion. The mobile drawer locks the page behind it and returns focus to the toggle on close.

Do & don't

Do

  • Use the tokens from base.css for every colour, radius and shadow.
  • Keep chips and pills fully rounded and one-line.
  • Use Inter for UI, Playfair for editorial titles.
  • Let whitespace separate blocks instead of borders and boxes.

Don't

  • Introduce orange or any second accent colour.
  • Hard-code hex values or pixel radii in page CSS.
  • Mix square and rounded chips in the same view.
  • Recolour the logo beyond the white inverse, or stretch it.