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. The mark's own teal anchors the palette: the deep ink carries actions and text, the brighter REISO teal holds the dark half. There is no orange anywhere in the palette.
  • Calm, editorial, legible. Generous whitespace, restrained colour and clear hierarchy keep long-form reading comfortable.
  • One hue, three jobs. The ink marks what matters (a link, an active filter, a kicker); the brand teal appears only where the surface is dark enough to carry it; the light tint fills soft blocks. No second chromatic accent.
  • 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 · #33747F The default: buttons, links, active states, borders, solid pills, icons.
REISO teal --brand · #62B2C0 The mark's colour: the 3px banner/hero seam, and on the dark half the footer headings and link hovers. Never on white, never for body text.
Light teal --primary-light · #EAF8FA Tinted backgrounds, soft badges, hover fills.

Ink & neutrals

Dark navy --dark · #1A3C42 Headings, dark banners, panel headers, hovers that carry white text.
Deepest navy --darker · #132B2F The footer surface and the deep end of the banner gradient.
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 De-emphasis and muted fills; also the range slider's inactive state.
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.

Explorer data-viz

Articles --viz-article · #1A3A4A The article crowd: the many small nodes that recede into the graph.
Auteur·es --viz-author · #D55E00 Author nodes: vermillion, de-emphasised by size, not by fading.
Dossiers --viz-dossier · #009E73 Dossier hubs: large, labelled and green.
Thèmes --viz-theme · #8E44AD Theme hubs: large, labelled and purple.
The four Explorer graph-node colours are an Explorer-only palette and must not be reused anywhere else - no chips, cards, charts or banners. They are defined as --viz-article (deep navy), --viz-author (vermillion), --viz-dossier (green) and --viz-theme (purple) in base.css, with --viz-link for the neutral hairlines between nodes. They deliberately step outside the teal family so the four node types are unmistakable on the white canvas (each pair at least ΔE00 24, and still at least 18 for the main colour-vision deficiencies). Keep them here: do not pull them back into the brand palette, and do not borrow them for other components. There is no orange and no second chromatic accent. The default is --primary (dark teal #33747F): links, kickers, dates, buttons, badges and solid surfaces that carry white text all use it. --brand (REISO teal #62B2C0) is the mark's own colour: the 3px banner seam and, on the dark half only, the footer headings and link hovers - it never touches white. --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 · 500 / 19–21px À Carouge, un projet invite les 12 à 30 ans à coconstruire un quartier en devenir.
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, article titles/headings, and the masthead of a page whose content is set in it (.page-head--serif - Fil de l'actu, Parutions, Sélections, Articles, the dossiers annuels) - the same flag that gives those pages' cards and timelines their face. Everything functional - navigation, buttons, meta, the page heads of documents and of the functional lists (Agenda, Emploi, Formation) - 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; the carnet rubrics (.portal-rubric, and the homepage block heads) replace that rule with a caps label and a hairline across the row. 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), and the lead that opens it is the next step up (.article-standfirst, 19–21 px Inter Medium on every detail page). 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

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 - the header and secondary CTAs both use 24 px, which reads as a pill at their 44 px height; 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, page head, content columns and sidebar all stop on the same margin at every width. The page head and the content sections use the same bar-model box (outer box --max-width, --gutter as padding), so the title and the copy below it share one left edge; the hero and detail covers stay full-bleed banners and give their own content the same gutter as an inner margin.

--max-width1344px (1280px content + 2 × 32px gutters)
--gutter32px desktop · 24px ≤1140px · 18px ≤768px - the single horizontal gutter
--header-h104px desktop · 64px ≤960px
page shellbackground white everywhere; content region 1280px, no side padding, no section card
contrastmain content flat; frames are reserved for structure - sidebar widgets (.panel), info cards and notices carry a 1px --line frame + --shadow-sm, while list content (carnet rows, tiles) is unframed; a 3px --brand seam sits under every banner/hero

Breakpoints

max-width: 1140pxCompact tier - 15px nav items, 24px gutter, two-column footer; sidebars become static.
max-width: 960pxHeader collapses to the burger menu; legal 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.
max-width: 768pxMobile - footer columns, full-width utility bar, single column.
max-width: 480pxSmall phones - stacked actions.

Page head

Every page opens on the page head (.page-head, rendered by partials/page-head.html - a content page calls it through the page-head shortcode): the title, a kicker where the page needs one (Erreur 404, Style guide), a standfirst of one or two sentences and any CTA (Insérer une annonce, Proposer un article), set directly on the white sheet - no banner background, no seam, no photograph. The action is an outlined secondary button (.page-cta .btn-secondary): S'abonner in the header is the site's only filled CTA, so the two never compete for the same corner. An index opens on the title of the list it holds, a document on the title of the document. An index is a table of contents, so the photograph belongs to the story it illustrates: 440px of stock photography above the first row is 440px the reader spends before reaching it, and photography lives in the homepage hero, the theme spotlight and the detail covers instead. The masthead sets its title in --serif when the page's own content is set in the display face (serif: true in front matter, or serif="true" on the shortcode: the reading lists and the dossiers annuels) and in --sans everywhere else. A page that still wants its own social card sets ogImage in front matter, which head.html reads.

List pages

Every list (Emploi, Agenda, Formation, Fil de l'actu, Parutions, Sélections, Articles, Communauté) shares one shell: the content section sits on the one page --gutter, the count and filter module sit in one toolbar - above the results on Articles, between the opening spread and the feed on the six portals and the community list - 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

Only the header and drawer S'abonner CTA (.btn-cta) is filled. Every other action on the site - a page head's CTA, a form's submit, the detail sidebar CTA, the affiche's Découvrir - uses the outlined .btn-secondary, so a page never shows a second filled button beside the header's. Both are pill-shaped, set in --sans at 15px/700, and the secondary one washes with --primary-light on hover rather than filling.

Header & navigation

The global header carries five high-scent primary items - Lire (a dropdown, grouping Revue - Nos articles, Le fil de l'actu, Les dossiers annuels - and Publications - Liste des parutions, Sélections des bibliothèques), Travailler, Se former, Agenda and Nous connaître (the second dropdown: À propos de REISO, Communauté, Nous contacter) - plus a filled S'abonner CTA (.btn-cta), styled from the teal tokens, and the account/search icons. It is the only filled button on the sheet: a page's own action is outlined (.btn-secondary, see Page head), so the two never compete. 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 group the same sections - Lire, Travailler & se former, Agenda & publications, Nous connaître - so no destination is lost between the two.

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, for a badge that belongs on the image), 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). A shape parameter renders the editorial variants from the same slots - feature, entry (the last one takes a gutter HTML fragment and showMeta) and tile (a frameless index tile: cover, meta line, title and a two-line description) - documented with the listing portals below. With no shape the card keeps its framed shell, which the shortcodes and the component docs use. 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).

The Formation portal fills the three card rows with the start date (Début 15.09.2026), the course title and the organisation with its canton (AGPsy · Genève) - the publication date stays on the detail page - and is ordered by start date, earliest first.

A second, collection-specific card - the cover with an article-count pill, plus the year or the theme name, and a description - is shared by the dossiers annuels timeline, the mobile thèmes list, the homepage dossier and theme spotlights and the article-detail sidebar. It is rendered by layouts/partials/dossier-card.html and layouts/partials/theme-card.html and its styles (.dossier-node*, .dossier-node-cover) live in components.css. On a dossier the credit caption sits directly beneath the cover; a theme card has no year and no credit. The title anchor's stretched ::after makes the whole card clickable while the credit keeps its own link (never an <a> inside an <a>). The credit itself (.photo-caption) is never 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 thèmes page presents its taxonomy in two exclusive forms (assets/css/pages/themes.css). On desktop it opens on a proportional collage, .themes-mosaic: one tile per theme inside a single rectangle, each tile's area equal to that theme's share of the archive. A row's height and a tile's width are both flex-grow on the same counts (the row's article total, the theme's count), so the area stays proportional at any width with no JS; themes are dealt into rows of four so every row samples the whole distribution, and a legibility floor keeps the least-covered tiles readable. A tile is the collection card in miniature - the cover with the shared thumbnail radius and hairline, then the caps count line in --text-muted and the name in the display face on the white sheet - with the name size following the tile's share of its row (data-size). Hover deepens the cover shadow and turns the name --primary, like every collection card; there is no tilt, lift or overlay. Under 900px the collage is hidden and the shared theme card renders as a plain column (.themes-list: one card per row, no timed rail).

The homepage is a front page of the same carnet. Every block is a caps rubric with a hairline, then a single ruled column (.card-grid--rows-1) rendered from the portal's own data and portal.entry config: offers with the logo gutter, articles with the cover thumbnail and serif titles, formations with the month gutter - followed by the shared dossier band and the theme spotlight. Nothing in the main column is framed: the rail's .panel widgets are the only surfaces with a border and shadow.

The tile shape is the frameless index card available to a visual taxonomy: a 3:2 cover with the thumbnail radius, a caps count line, the name and a two-line description. Only the title turns teal on hover. The homepage no longer uses it: its themes block is a single featured theme in the shared collection card, drawn at random on each page load from the whole thèmes list, with the theme name and article count and a link to the Articles archive filtered on that theme.

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.

Document prose

The long-form layer shared by the document pages (Conditions, Mentions légales, Protection des données, Impressum and the Consignes rédactionnelles): a two-column card whose main column is a run of .block sections using .prose (16 px reading measure) and .prose-list, with a .panel + .doc-nav sommaire in the sidebar and a .doc-update provenance line closing it. Long clauses collapse under .clause (a native <details>). Anchored sections carry a scroll-margin-top, so the sticky header never covers a jumped-to heading. .panel-link is the quiet link closing a sidebar panel - used by Proposer un article to point at the guidelines.

Example document prose (.prose) - 16 px, 1.7 line height, links underlined with a --line rule that turns --primary on hover.

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 À 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. A page may set portal.sortBy to an item key - dotted keys reach a facet, e.g. facets.debut - to fix the display order (ascending by default, sortOrder: "desc" to reverse it); the Formation portal is ordered by start date, earliest first, and grouped into month bands on facets.debut through groupByKey (bands otherwise derive from facets.date). A calendar page may also set portal.upcoming: true to drop items whose schedule has ended - the end bound is facets.date-end when present, falling back to facets.date - so the Agenda bands start at the current month and its lead holds the next events. A page may also set portal.lead to repeat the N newest items in an opening block (.portal-lead, headed by a .portal-rubric) above the list - the feed keeps every item, so an opening story is also a feed row; leadTitle names that opening, while feedTitle stays as the feed's visually hidden heading in the carnet (no second section marker on the page) - "newest" means "soonest" when sortBy ascends a start date, which is how Agenda leads with its 4 next events - and combine it with portal.groupBy: "month" to group the list under month bands (h3.portal-month, beneath the page and zone h2s) derived from facets.date; Fil de l'actu uses lead: 4 plus a descending sortBy: "facets.date", a month feed (.portal-month--feed) and grid: "rows", which turns the lead into the opening spread and the feed into the carnet described next.

The editorial carnet (portal.grid: "rows") drives the six portals - Fil de l'actu, Agenda, Parutions, Emploi, Formation and Sélections - and both the Articles archive and the Communauté list mirror it in their own layouts, so every list opens with a front page instead of a wall of identical boxes. The opening spread pairs one card--feature (a headline at clamp(27px, 2.9vw, 38px), or clamp(22px, 2.2vw, 32px) with portal.featureSize: "compact" for long bibliographic titles, the standfirst and one foot line holding the meta and the facets) with a .portal-rail of the next two stories in the same compact card--entry anatomy as the feed (44px gutter, 18px title, two-line standfirst, at most two pills). Then the toolbar - search, filters, and the live count as the feed's result tally - then the feed: a two-column .card-grid--rows of flat entries with a hairline above and no frame, radius, shadow or lift; only the rule, the title and the gutter turn --primary on hover. The first rail row aligns with the feature title, and the feed follows the toolbar with no second section marker (the .portal-feed keeps a visually hidden h2 for the document outline). Month bands (h3.portal-month--feed) stick under the header on desktop and go static on mobile, and the filter engine hides a band whose entries are all filtered out. The opening spread repeats the feed's first stories and is shown only while nothing is filtered: every item is also a feed row, so filtering hides the opening but never removes an item from the list, and the count, empty state, timeline rail and facet pills describe that full list. Articles swaps the date gutter for the article's cover thumbnail (an empty tinted plate when no image is on file), so its rows scan the same way. Communauté replaces the lead with an “En lumière” spotlight: three authors with a portrait, one feature plus a rail of two, picked at random on every page load by assets/js/communaute-featured.js from the pool rendered by partials/people/featured.html; the spotlight repeats three people who are also in the alphabetical list below (so its counts are unaffected) and disappears as soon as a filter is active. Its person rows put the portrait in the margin and keep the name, function, article count and type pills, and the feature adds the person's bio. The feed also carries the shared index rail, keyed by the first letter of the name under Nom and by article-count bands under Plus actifs.

The margin gutter carries each list's ordering key, set per page through portal.entry and rendered by layouts/partials/portal/gutter.html: gutter: "date" gives a day and abbreviated month, "month" a month and year, "year" the four-digit year, and "region" a region code plus a sub-value (gutterSub: "rate", gutterSubSuffix: "%"). gutterKey names the facet read (default date). Month abbreviations are ambiguous to speech, so the visible ones are hidden from assistive tech and a full French date or label is supplied instead. entry.meta shows or drops the item's own meta line (a date range, a type, a month), entry.pills lists the facets rendered as chips - their labels come from the page's filter options, so the vocabulary is still written once - and entry.pillsMax caps them, with the opening rail capped at two.

Row media. A list can give every row a media gutter with entry.media: "logo" renders the item's logo in a fixed 96px tile (72px in the opening rail, 80px on mobile) on a white surface, with the region and rate beneath it on Emploi, and logoDark: true flips the tile to --dark for a white mark (PINOS). An offer without a logo falls back to an initials plate (monogram, or derived from the excerpt), so a mixed list keeps one text column and never looks broken. "cover" renders the item's image as a 112px cover (Sélections). The opening feature shows the same mark larger above the headline. Employer marks keep their own brand colours - they are content, not palette accents - and are used unmodified; provenance lives in docs/logo-sources.md.

The results count (.portal-count - a serif number plus a caps .portal-count-label) and the filter module come from portal/toolbar.html. Typeface follows the content, not the layout: the reading lists (Fil de l'actu, Parutions, Sélections) set their titles in Playfair via serif: true, the Articles carnet uses the same serif in its own layout, while the functional listings (Agenda, Emploi, Formation) stay in Inter. Articles renders the carnet from that archive layout, whose client-side index mirrors the server markup, while the Communauté list renders the same carnet with its person-card shapes. The two shapes are rendering modes of the one card.html partial, not new components, and every item keeps the .card class, so the filter engine and the year rail are untouched.

Fil de l'actuday + month gutter · month bands · theme pills · serif · year rail
Agendaevent day + month gutter · date range and venue as meta · month bands · type/format chips · sans
Parutionsyear gutter · type as meta · compact feature · serif · year rail
Emploilogo tile + region/rate gutter (initials plate when no logo) · posting date as meta · type and domaine pills · sans
Formationmonth + year gutter from facets.debut · month bands · theme and length chips · sans
Sélectionscover-thumbnail gutter · month as meta · serif · year rail
Communauté“En lumière” author spotlight · A-Z / article-band rail · person card

The toolbar's count (.portal-results-title.portal-count - a serif number plus a caps label, composed from countLabel with the number in [data-portal-count]) closes the carnet toolbar below the search; on Articles it still opens the page. 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 Liste/Explorer and the communauté list for sorting by Nom or Plus actifs. There is no “Rechercher” button: the six portals, Articles and the communauté list use the editorial carnet above. Typing filters the feed 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); the fixed opening is never filtered or highlighted. The sticky .portal-timeline rail indexes the feed from the visible cards - years on the date lists, and on Communauté the first letter of each name under Nom or the article-count bands (30+, 20–29, 10–19, 5–9, 1–4, 0) under Plus actifs - with counts that follow the filters; a full rail on desktop, a compact column on tablet, and a horizontal sticky strip on mobile. The feed repeats the same keys as sticky section bands (h3.portal-month--feed), rebuilt by assets/js/communaute.js after each sort and hidden when a filter empties them. The opening spread keeps the full content width: the rail shares the .portal-results-body grid with the feed and starts beside it, below the opening, so it never squeezes the feature or the rail of stories. Articles and the community list use the carnet's .card-grid--rows - two columns on desktop, one below 1080px - with their own row cards; 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) whose card--entry rows lead with the portrait (an initials monogram when no photo is on file) and add the name, the function, the total article count and the type pills, while card--feature adds the person's bio. The homepage uses the same carnet rows - .card-grid--rows-1, one ruled column inside the 70% main column - with each block reading its portal's data and entry config. Month bands are requested with groupBy: "month" (.portal-month, derived from each item's facets.date or the groupByKey facet; the engine hides a band whose cards are all filtered out) - Fil de l'actu, Agenda and Formation use them. 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

Editorial carnet - opening spread & ruled feed

The live example below is the grid: "rows" mode used by the six portals and the community list, shown here with Fil de l'actu data: an opening spread (card--feature + .portal-rail of compact card--entry rows), then the count and search toolbar, then a two-column feed of the same card--entry rows. Hover a row to see the one state it has - the rule, the title and the gutter turn --primary.

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 - for a value (a course length, a deadline) that must stay at the top-right of a framed card instead of wrapping 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--rows and .portal-timeline; the D3 graph (assets/js/articles-explorer.js) renders article/author/dossier/theme nodes in .article-explorer. Colours come from the Explorer-only data-viz tokens (see Colour): articles --viz-article, authors --viz-author, dossiers --viz-dossier, themes --viz-theme. They are never reused outside this graph.

Person

One shared person card, layouts/partials/person-card.html (.card--person), drives the Communauté list and the Comité / Équipe rows on À propos. Its shape selects one of three layouts: a card--entry carnet row (the portrait in the margin gutter, the name, the function, the total article count and the type pills), a card--feature spotlight head with the bio, or - without a shape - a framed portrait card. A row that must keep the bio opts in with bio: true; the À propos lists do, the community feed does not. The whole card is one link.

A standalone person card, layouts/partials/person.html (exposed as the person shortcode), stays for contact blocks that are not part of a list: image (a portrait; when omitted a monogram in the brand teal is drawn from the initials), name, description, tag (an optional kicker pill) and href, laid out in a .person-grid. It is used for the contact people on Proposer un article, Insérer une annonce and the consignes rédactionnelles.

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 body opens with the lead, .article-standfirst - 19–21px Inter Medium in --text, on the same 760px measure and 32px above the prose: whitespace and type set it apart, with no rule and no indent, so it stays on the one page gutter. A wide: true page drops the measure on the lead too, so it never sits narrower than the copy it opens.

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

À Carouge, un projet invite les 12 à 30 ans à coconstruire un quartier en devenir. Ses particularités : il combine des opportunités immédiates de financements avec des développements d'infrastructures à long terme.

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 homepage, the mobile order is the first block (job offers), then the “fil de l'actu” and “prochains rendez-vous” cards, then the remaining blocks - all ordered with CSS (no post-load move), 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 (#33747F) for text and links - it meets AA on white at 5.3:1, and on the --primary-light tint at 4.9:1.
  • White on teal. A teal surface that carries white text uses --primary (#33747F) - the AA-safe pair (5.3:1). --brand (#62B2C0) measures 2.4:1 on white, so it never carries text, links or icons there; on --dark it reaches 4.9:1 and on --darker 6.1:1, which is where the footer headings and hovers use it. A --brand fill may only carry --dark text (4.9:1).
  • 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.
  • Break or aside a sentence with a hyphen (-), never an em dash.

Don't

  • Introduce orange or any second accent colour.
  • Use the bright REISO teal (--brand) on white, for text, links or icons, or as an active/state mark - it measures 2.4:1 there.
  • 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.
  • Use an em dash (U+2014) in any copy, label or comment.