Design guide
The visual language of dandelion.events as it exists in code today. This page is rendered with the site's own app.css and bootstrap5.css, so swatches, type and components are the real thing. The site is Bootstrap 5.3.8, restyled through its variables.
The root font size is 17px (19px from 1680px, 21px from 1920px), so rem values scale with the viewport. Sizes are in rem (or em, to follow the text); px is kept for 1–2px hairlines, shadows and the focus ring, for breakpoints and the container max-widths that fit inside them, and for overrides that must line up with Google Maps. Avatars take a size class (.avatar-xs to .avatar-2xl), not a width. Colour values, radii, type sizes and the spacing scale are measured live; other sizes are copied from the stylesheets.
Colour
Theme
A ramp generated from one value withoklch(). Components use the 500 for fills, 600 for hover, 100 for alert, toast and badge backgrounds, and 800 for text on those tints. Organisations and events set --theme-500 only (through /theme.css). Tints (50–200) sit at fixed lightness, so they look equally light across hues; shades (400–800) are relative to the 500, with 800 capped dark enough for text on tints. The 400 is .06 lighter than the 500, or half way to the 200 when the 500 is too light for that, and the 300 is halfway between the 200 and 400, so the steps stay in order however light the colour. Every theme colour is a solid step of this ramp; the only translucent ones are the focus ring and .badge-overlay (the 500 at 70%, over images).rgba(var(--bs-*-rgb)) colours need an rgb triplet, so /theme.css also sets --theme-500-rgb.Lavender
The default site's AI well and.lavender-wash backgrounds. Its steps come from --color-lavender-500, the default green turned to hue 300 at the same lightness and chroma, with the theme ramp's formulas.Yellow
For highlighted or rewarded things, throughtext-bg-yellow: Featured, Co-host, rota points, PATCH routes. Its steps come from --color-yellow-500 with the theme ramp's formulas. Yellow's 500 is the lightest (see Warning and danger), so its 200 to 500 sit close together, set apart more by saturation than lightness.Warning and danger
Two families, used the same way as the theme ramp: each variant class (alert-warning, toast-warning, table-warning, btn-warning, text-bg-warning and the danger ones) sets the steps it uses as--variant-*, and each component maps those to its roles once. The 500 is also Bootstrap's $warning and $danger in bootstrap5.scss, so text-danger, form errors and the like match. The lavender, yellow, orange and red 500s share the default green's oklch chroma, .169, so they are equally saturated; only lightness varies by hue, so each reads true: red .63, green and lavender .661, orange .76, yellow .86.Greys, black and white
Bootstrap's own variables, set in bootstrap5.scss to near-neutral greys: Bootstrap's lightness steps with a trace of warmth (designed at oklch hue 75, chroma .00225–.003, then rounded to hex). Usage: 100 (--bs-tertiary-bg) body.canvas pages, wells and hovered dropdown, autocomplete and list items, and table stripes · 200 (--bs-secondary-bg) hovered nav links, table rows and secondary buttons, secondary badges and input add-ons · 300 (--bs-border-color) table and secondary button borders · 400 input borders and hr · 500 sidebar sub-menu links and grey borders (comment boxes, the editor) · 600 secondary text · 700 input add-on text · 800 search bar · 900 header and sidebar. Body text (--bs-body-color) and emphasis text (--bs-emphasis-color, set to 900 in bootstrap5.scss in place of Bootstrap's black) are the same as 900. Black and white with transparency use rgba(var(--bs-black-rgb), a) and rgba(var(--bs-white-rgb), a), e.g. shadows and image overlays. Hairlines (card, dropdown, autocomplete and cookie banner borders, date tiles, the .chill card header's divider, marketing cards) use --hairline, black at 10%: Bootstrap's --bs-border-color-translucent, set in bootstrap5.scss, so its cards and dropdowns share it.Accents and surfaces
Single colours with one role each.Other gradients
The dark header and sidebar are a sunset: near-black fading to --color-midnight, with the orange-to-yellow --gradient-active as the sun on the active sidebar item, the notification dot and the load bar. Light content marks active states with the theme colour, so each surface has one active colour. --gradient-rainbow is only for .gradient-text headlines on marketing pages and the signed-out home.Marketing pages
The features and comparison pages. Their purple gradient and the five reason colours on the Luma page are derived from--color-midnight and --color-magenta-500. Rose and magenta are only used here, so they are defined in marketing.css, which only these pages and this guide load.Third-party brands
Only for buttons and labels that stand for these services.badge-payment-<method>:
Coinbase
Mollie
PayPal
GoCardless
GoCardless instalments
OC: …
EVM: …
Typography
.strapline and .header-font.b/strong. Line height 1.5.Scale
h1 · .fs-1
Gatherings near you
--font-size-3xl · 2rem ·
h2 · .fs-2
Gatherings near you
--font-size-2xl · 1.66rem ·
h3 · .fs-3
Your upcoming events
--font-size-xl · 1.4rem ·
h4 · .fs-4
Upcoming birthdays
--font-size-lg · 1.125rem ·
h5 · .fs-5
Ticket types
--font-size-sm · .875rem ·
.lead
An introductory line, like a sold-out card's
--font-size-lg · 1.125rem ·
.readable
--font-size-base · 1rem ·
body · nav · .btn
--font-size-sm · .875rem ·
.small
.875em of its context ·
.badge
.9375em of its context ·
.breadcrumb
--font-size-xs · .75rem ·
.h1–.h5 give any element a heading's style, e.g. <h4 class="h2"> for block titles. h5 is the smallest heading we use; h6 is the same size, so it's left to user-written descriptions. The heading sizes are set in bootstrap5.scss ($h1-font-size…), so .fs-1–.fs-5 make text a heading's size without its weight or font. .small is Bootstrap's class for smaller text without the meaning of <small>, which is for small print. Secondary text is --bs-secondary-color (--bs-gray-600), set with .text-body-secondary; form-text takes it too. .subtle is for links in it, which turn the theme colour on hover. .underline-dotted is for quiet links that read as part of a sentence (a rota's edit links) and text to copy.Radius, elevation, spacing
Radius
Bootstrap's radius variables, set to one scale inbootstrap5.scss: controls on the base, surfaces on lg, sheets on xl. Inputs and buttons share a radius at every size (sm, base, lg), so they match side by side. Square corners (0) where an element sits flush against another; 50% for circles.Elevation
Bootstrap's three shadows, set inbootstrap5.scss and used through its variables or the shadow-sm, shadow and shadow-lg classes. Soft, in px like hairlines: sm is a tight contact shadow (1px) alone, and the base and lg add a wide, faint one under it. Floating menus and panels (dropdowns, autocomplete, the cookie banner) also have a --hairline border, so the shadow can stay light; toasts and the chat panel have the shadow alone.Resting surfaces: header, search, cards (with shadow-sm), the editor toolbar while stuck
Floating: dropdowns, autocomplete, toasts, cookie banner, chat, hover lift
Not used yet
Spacing
One scale for padding, margins and gaps, as--space-* in app.css. Bootstrap's spacing utilities are the same scale without sm and xl, so in markup use the utility and in CSS the token. In app.css, values off the scale mostly size a control to match another (.375rem, btn-sm's padding, the nav crumbs' .625rem), line up with a fixed layout dimension (the header, the sidebar, search results' image column) or are px hairlines. The page-specific stylesheets are on the scale too, except for the same kinds of value: the docs select's room for its caret and the search icon's inset, and marketing sections' 3.5rem and 5rem, which are layout rather than spacing.--space-2xs
·
*-1
--space-xs
·
*-2
--space-sm
·
—
--space-base
·
*-3
--space-lg
·
*-4
--space-xl
·
—
--space-2xl
·
*-5
lg · mobile basesmbase each sidebasesm basemb-3 · basesm · table-sm xs540 / 720 / 960 / 1140Page shell
Fixed dark header (--header-height, 2.8125rem) and a fixed sidebar (--sidebar-width, 14.375rem, minified to --sidebar-width-minified, 3.75rem). Sticky panels (.sticky-under-header) sit at --sticky-top, just below the header. Below 768px the sidebar becomes a full-width offcanvas and header icons spread across a second row.
Maya
Components
Buttons
Primary and danger are flat: their variant's 500 over a transparent border, and the 600 on hover. Other fills (progress bars, text-bg-* elements other than badges, like card headers, and p.action) are the same flat 500. btn-warning is styled the same but not used yet. Secondary is quiet: white with a --bs-border-color border and body text, --bs-secondary-bg on hover. It's for dismissive actions like Cancel, Not now and Disconnect. btn-outline-secondary is only for buttons joined to an input (Apply, Remove, Copy) and the donation toggle group. Brand buttons like btn-signal stay flat.Badges
Bootstrap's.badge with a text-bg-* colour, for counts, statuses, tags and amounts. Sized in em, so a badge scales with the text around it: small in a nav, bigger beside a heading. Pick the colour by what it means.Full moon gathering Featured Locked
| Class | Means | As used |
|---|---|---|
text-bg-primary |
Good, or the main count | 1286/6breathworkKitchen shift |
text-bg-secondary |
Neutral detail | LockedSecret£5 discount applied |
text-bg-warning |
Needs attention, not yet a problem | On hold |
text-bg-danger |
A problem or a hard limit | Sold outSales endedFullRejected, to contact |
text-bg-yellow |
Highlighted or rewarded | FeaturedCo-host 2 |
text-bg-dark |
Counts in a gathering's nav | 312/20 |
text-primary border border-primary |
Outline: a group label, or an unselected choice | AvailableWeekend passLondon |
text-bg-stripe |
Reported by Stripe | £1,240 |
text-bg-yellow and text-bg-stripe are ours, written like Bootstrap's. Payment badges (badge-payment-<method>) are under Third-party brands. A badge with no colour class has no background, so always give it one (or an inline background, as the hashed organisation and gathering badges do).Alerts
Toasts
Forms
Navigation crumbs and dropdown
Input groups
File and image inputs

Select2
Flatpickr
Rich text editor
CKEditor 5, from our own build (symbiota-coop/ckeditor5-dandelion).wysiwyg_block renders a textarea.wysiwyg, and app.js swaps in the editor. ckeditor.css styles it and the rich text it produces; the build adds two paragraph styles, Lead and Small.Filter forms
The search forms above lists and tables: orders, donations, members, applications. Bootstrap 5 dropped.form-inline and .form-group, so app.css keeps them for these: .form-inline lays the form out in a row from 576px, and each .form-group is a label and its field side by side. .searchForm spaces them and caps fields at 12em; .submitOnChange submits when a select, date, checkbox or month field changes.checkbox 'Deleted') is a .form-group holding a .form-check-inline · in grid rows instead (events/_search_form.erb), each .form-group takes a col, with the checkboxes beside the button on a second row, inside a .search panel: --bs-tertiary-bg, --bs-box-shadow-sm · .select-placeholder greys a select until app.js loads, which then greys any select while its chosen option is a disabled placeholder · popular locations: outline badges, the current one filledShort URL input
Theme colour picker
Add block
Button groups
Card with .chill header
Your upcoming events
Table and pagination
| Ticket type | Sold | Price |
|---|---|---|
| Early bird | 40/40 | £15 |
| Standard | 23/80 | £22 |
| Solidarity | 9/10 | £8 |
Key-value table
| maya@example.com | |
| Website | https://maya.example.com |
| Location | Hackney, London |
Striped table
| Kitchen 2 | Welcome desk 1 | |
| Friday | ||
| Saturday | Sign up | |
| Sunday | Sign up |
Tooltips
London
Sat 4 Oct, 7–10pm
Progress and loading
Charts
Chart.js, with white bold value labels from chartjs-plugin-datalabels.Tabs and pills
Modal
Search page
The /search page (search.erb), and lists of people like birthdays. A large input with a round clear button, a row of underlined tabs, and results between rules.Header search and autocomplete
Notifications and messages dropdowns
Messages
Mobile bottom sheet
Date and icon tiles
The event page's date, location and link, as tiles beside the details they stand for. On the site they're CSS (.event-detail in app.css, over event_details.css): white, a --hairline border, --bs-box-shadow-sm, .3em corners, 2.5em square; the month a bold, letter-spaced band of --theme-50 in --theme-800 over a semibold day; icons 1.5em in black at 50%. On hover the tile turns solid theme. Emails can't rely on that CSS, so they show the same tiles as PNGs fromrake icons:generate (app/views/icons), at 2.75em with the tile inset to match: the month band green for the default theme, grey for events and organisations with their own colour (the location and camera tiles are the same in both).Event block
How events are listed on organisation and search pages (events/_block.erb, with _block_main.erb). From 992px the image sits on the right and the row gets a --bs-tertiary-bg panel with base radius; below that the image stacks on top.- Maya and Sam
- Sat 4 Oct, 7–10pm
- Hackney, London
Carousel
Featured events and the organisation's own carousels (upcoming events, plus past ones with recordings) on organisation pages (events/_carousel.erb), with Slick: three to a slide from 992px, two from 576px, then one.Ticket purchase form
The Select tickets card on an event page (purchase/_purchase.erb), one row per ticket type from purchase/_ticket_type.erb, then discounts, a donation and the total down the right-hand column. Shown here without purchase.js, so totals don't add up.Select tickets
| Weekend | ||
| Standard | £22 | |
| Solidarity 2 left | £8 | |
For those on a low income. No questions asked. | ||
| Day passes | ||
| Pay what you can |
£5 - £30
Drag the slider
|
|
| Early bird | £15 | |
|
Apply a code
|
||
|
Add a donation to Dandelion
£
|
||
|
Total
£
|
||
Comments
Lift shares from London
Avatars
size: (:sm by default): xs 2rem in comments, sm 3.125rem in lists of people (the most common), md 4rem in message threads and map info windows, lg 4.6875rem for team and gathering members, xl 6.25rem in account rows, 2xl 9.375rem beside feedback quotes, fill to take its column's width · img.private at 50%, full on hover · .profile-photo, the large photo at the top of a profile or application: 12.5rem, framed in whiteRatings and feedback
— Thank you so much for coming!
We loved having you, and we'll be running the next one at the end of the month, with a longer sound bath.
Image wall
Calendar
An organisation's events by month or as a list (events/_calendar.erb), with FullCalendar. Events are --theme-500, or in embeds the organisation's own colour, so the grid has one colour: today's circle and the events. Each has a tooltip with its location and time.Timetable
Morning yoga
by MayaMap markers
Map info window
MayaHackney, London |
Email previews
Cookie banner
Empty states
No upcoming events
p.action
A centred call to action for docs and email previews, where a .btn class can't be added. Drawn like btn-primary; emails use their own in email.css.AI well and ask box
The AI well is the only sheen, so the gradient itself marks AI: 135°,--ai-well-from flat to halfway, then to --ai-well-to. That's --color-lavender-50 to --color-lavender-400 on the default site, and the theme's 50 to 400 on themed pages (theme.css). Padding 1rem, radius lg.A gentle evening of breathwork and sound for all levels.
Theming
Organisations and events set one colour. theme_css_variables sets --theme-500, app.css derives the ramp from it, and every primary button, link, focus ring, crumb, check, progress bar, stats chart, alert and toast tint and date tile month follows. theme.css is only variables: as well as the theme colour, it switches the AI well to the theme ramp (see AI well and ask box), while the default site keeps it lavender as its own accent.
/theme.css sets on :root, from theme_css_variables(clamp_color(colour)), and has the theme-scope class so the ramp and focus ring are recomputed from its --theme-500.
