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.

01

Colour

Theme

A ramp generated from one value with oklch(). 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).
--theme-* · Primary and success · follows each organisation's theme
50
crumbs, newsfeed icons, date tile months, docs transcripts, themed AI well
100
alert, toast and badge background, crumb hover, unread messages
200
progress tracks, blockquote and transcript borders
300
chart projections
400
themed AI well end, Stripe Connect fees in the contributions chart
500
600
hover
800
text on tints
Bootstrap's 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.
50
.lavender-wash, AI well background, features page section wash
100
not used yet
200
not used yet
300
not used yet
400
AI well gradient end
500
not used yet
600
features and Luma pages: purple divider end, call-to-action glow
800
not used yet

Yellow

For highlighted or rewarded things, through text-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.
50
not used yet
100
badge background
200
not used yet
300
not used yet
400
not used yet
500
contributions chart line and fill; text-bg-yellow on anything but a badge (not used yet)
600
not used yet
800
badge text

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.
Warning · --color-orange-*
50
row background. Also: features page section washes
100
alert, toast and badge background
200
row border. Also: features page divider end
300
not used yet
400
Star icons, feedback stars, map areas, features page
500
fill: buttons, card headers and other text-bg-* elements that aren't badges. Also: ASN stats: bot % of 30 and over
600
button hover
800
text and links on tints, badge text
Danger · --color-red-*
50
row background
100
alert, toast and badge background
200
row border
300
not used yet
400
Map: event and organisation markers
500
fill: buttons, card headers and other text-bg-* elements that aren't badges. Also: ASN stats: bot % of 60 and over; check-in QR scan line
600
button hover
800
text and links on tints, badge text
Danger badges take the 200, as red's 100 reads pink. table-warning and table-danger rows mix 30% of the way from the 50 to the 200 for hover, stripes and active rows.

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.
--bs-white
--bs-gray-100
--bs-gray-200
--bs-gray-300
--bs-gray-400
--bs-gray-500
--bs-gray-600
--bs-gray-700
--bs-gray-800
--bs-gray-900
--bs-black

Accents and surfaces

Single colours with one role each.
--color-paper
.lavender-wash end
--color-highlight
mark, .highlight
--color-midnight
header sunset; root of the marketing pages' purple

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.
.lavender-wash
Lavender to paper. body.lavender-wash on the sign-in pages (email, code, Ethereum), sign-up and auth failure; a .lavender-wash div, which also fades to white at the bottom, behind the hero on the marketing pages and the signed-out home.
--gradient-active
Sidebar active item, notification dot, page-load bar.
--gradient-rainbow
.gradient-text only.
--gradient-header
The header: 45°, --bs-gray-900 to 66%, then midnight.

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.
Section washes
· · ·
section backgrounds: the 50 step of orange, rose, magenta and lavender
--color-rose-500, …
·
section divider: 500 to 200
--color-magenta-500, …
·
section divider: 500 to 200
Magenta to midnight
color-mix, 0–100%

Third-party brands

Only for buttons and labels that stand for these services.
--color-evm
EVM
--color-stripe
Stripe · text-bg-stripe
--color-opencollective
Open Collective
--color-coinbase
Coinbase
--color-signal
·
Signal · btn-signal
--color-paypal
PayPal
--color-gocardless
·
GoCardless
--color-mollie
Mollie
Payment badges, from badge-payment-<method>: Coinbase Mollie PayPal GoCardless GoCardless instalments OC: … EVM: …
02

Typography

HEADINGS · --header-font-family
Hanken Grotesk
Variable 100–900, used at bold. Letter-spacing −0.02em. Also .strapline and .header-font.
BODY · --body-font-family
Plus Jakarta Sans
Default weight 500, labels 700, bold for 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
Long-form copy such as event descriptions sets at 1rem.
--font-size-base · 1rem ·
body · nav · .btn
Body text, inputs, tables, buttons (btn-sm too), nav links, tabs, dropdown items and tooltips
--font-size-sm · .875rem ·
.small
Secondary details: a location, a description
.875em of its context ·
.badge
Badges
.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.
03

Radius, elevation, spacing

Radius

Bootstrap's radius variables, set to one scale in bootstrap5.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.
--bs-border-radius-sm
form-control-sm and btn-sm, Select2 tags
--bs-border-radius
Inputs, buttons, button groups, alerts, toasts, pagination, tooltips, sidebar pills; the bottom corners of menus that hang from their input (autocomplete, Select2)
--bs-border-radius-lg
Cards, modals, menus and panels: dropdowns, cookie banner, AI well, ask box. Also form-control-lg and btn-lg
--bs-border-radius-xl
Mobile bottom sheet (top corners), marketing cards, chat bubbles
--bs-border-radius-pill
Badges, nav crumbs, the header avatar, round buttons and dots

Elevation

Bootstrap's three shadows, set in bootstrap5.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.
--bs-box-shadow-sm
Resting surfaces: header, search, cards (with shadow-sm), the editor toolbar while stuck
--bs-box-shadow
Floating: dropdowns, autocomplete, toasts, cookie banner, chat, hover lift
--bs-box-shadow-lg
Not used yet
The mobile event page's fixed tickets bar keeps its own upward shadow.

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
Label to field, dropdown and autocomplete items, sidebar sub-menu, badges in a row (mb-1, gap-1)
--space-xs
· *-2
Nav links, dropdown headers, search tabs, filter form fields, cookie banner buttons
--space-sm
· —
Grid gutter, table cells, card headers, alerts down, notification items, mobile sidebar links
--space-base
· *-3
Between fields (mb-3), mobile --content-padding, dropdown and list items across, alerts across
--space-lg
· *-4
--content-padding, below a carousel
--space-xl
· —
Dandelion Daily: between article columns and articles · docs h2s above · comparison cards (marketing) · message bubbles from the far side
--space-2xl
· *-5
Between page sections (mt-5, mb-5)
Content padding · --content-paddinglg · mobile base
Grid guttersm
Container paddingbase each side
Card bodybase
Card headersm base
Field spacingmb-3 · base
Table cellssm · table-sm xs
Containers540 / 720 / 960 / 1140
04

Page 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
Home
Dandelion Daily
Eventshover
Organisations
Symbiota All organisations
#content · padding --content-padding
col-lg-8 · newsfeed
col-lg-4 · sticky
05

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.
.btn · .875rem · weight 600 · padding .5rem 1rem (sm .375rem .75rem), the same height as an input · radius .5rem (--bs-border-radius), as inputs

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

Followers 128 · Tagged breathwork in Weekend pass · Paid £5 discount applied
.badge · .9375em · .4em .85em · pill · weight 600 · primary, secondary, warning, danger and yellow are soft: their variant's 100 behind its 800, so they show on grey as well as white (danger: the 200, as red's 100 reads pink; secondary: --bs-gray-200 and 800); dark, stripe and payment badges are filled · .badge-overlay, for labels over images (the carousel), is filled with its variant's 500 at 70%, white text, square
ClassMeansAs 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

Your ticket is booked. View order
Only 3 tickets left. Book now
Payment failed. Try again
Inline, in the page. Padding .75rem 1rem (sm base, like card headers), no border. Links bold and underlined in the alert's link colour. Flashes are toasts, except on the few pages that set @hide_alert (sign in, sign up, subscriptions, the mailer header), which render them inline as alerts with no close button.

Toasts

Your ticket is booked.
Only 3 tickets left.
Payment failed.
Flash messages, fixed at the top centre (_alert.erb). The same flat tints as alerts, with no border, plus a --bs-box-shadow. Notices fade after 3s, errors and warnings after 6s.

Forms

Email is invalid
Form text is --bs-secondary-color. Every focused control gets the same ring, --focus-ring: --bs-focus-ring-width (2px) of --bs-focus-ring-color (the theme at 60%). Danger buttons ring in red, btn-signal in its blue, and colour swatches add a white gap. Nav links have none.
input .875rem with .5rem vertical padding, the same height as a button (about 2.4rem) · border --bs-gray-400 · labels 700 · errors: lib/form_builder.rb adds is-invalid and an .invalid-feedback message in --bs-danger, the field name first · .field-note, for a note or link under a field (a preview link, a resend link), pulls it up into the field's mb-3 so it reads as the field's own

Navigation crumbs and dropdown

Input groups

£
input-group-text for currency symbols (money_symbol) and inline labels · a button on the end for one-field forms (sign in, search, reactions) · d-inline-flex w-auto when the field is narrow · input-group-sm with form-control-sm

File and image inputs

file_block and image_block (lib/form_builder.rb): a plain form-control file input, with the current file above it once saved: a download link, or for images the image at up to 12.5rem high, then a Remove checkbox (and Rotate by for images) · app.js rejects files over 10MB before upload

Select2

Press comma to add a tag or enter to accept a suggestion
select2.css overrides select2-bootstrap-5-theme: as tall as .form-control, --bs-gray-400 border, .form-control's font and radius · focused or open: --theme-500 border and --focus-ring, the box and dropdown glowing as one shape · selected option --theme-500 · tags (_tags_select.erb) sit on the typing row, radius sm

Flatpickr

app.js wires up .datepicker (altFormat Y-m-d) and .datetimepicker (J F Y, H:i, 24-hour; data-flatpickr-min-date, -max-date and -linked-end) · selected days and range ends --theme-500 · on phones, a native input (.flatpickr-mobile) at full width

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.
Fields stack below 576px, with the button below them · the checkbox helper (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 filled

Short URL input

https://dandelion.events/o/
shorturl_block (lib/form_builder.rb), for slugs on the build pages: the address's stem as text, then an inline input with only a dotted --bs-gray-400 underline and no focus ring, 15em wide, so it reads as part of the address · a link icon once saved · in .slugify, it fills in from the name

Theme colour picker

_quick_theme_colors.erb, beside the colour field: quick_colors steps from the default green round the wheel in thirteenths, keeping nine, at HSL lightness 50% · 1.625rem circles that grow 10% with a --bs-box-shadow on hover · selected or focused: a white gap, then a ring in the swatch's own colour

Add block

organisations/_add.erb and accounts/_add.erb: plus and minus icons under a list of people or organisations, each opening a small form · .add-block hides the field's label, since the placeholder says it · white, --bs-box-shadow-sm, base radius · removing is a select and a btn-danger

Button groups

Add a donation to Dandelion
Joined buttons for a choice of amounts (the purchase form's donation: btn-outline-secondary like the Apply button beside it, the chosen one .active, filled --bs-gray-600) and paired tiers (accounts/_pay.erb) · dropdown_nav in a btn-group is the Actions menu on order and ticket tables: an item with no path is a .dropdown-header, bold --bs-emphasis-color over a --bs-border-color line (none in nav crumbs or the mobile bottom sheet, whose sections have dividers), and nil a divider

Card with .chill header

Your upcoming events

Ecstatic dance on Sat 4 Oct
2 hours ago
Maya followed you
Yesterday
All events →

Table and pagination

Ticket typeSoldPrice
Early bird40/40£15
Standard23/80£22
Solidarity9/10£8
Row colours: table-warning flags a row to look at (low delivery rate), table-danger a problem (blocked ASN). Both use the warning and danger 50 and 200
Active: --theme-500, like the active pill and dropdown item · Hover: --bs-secondary-bg, like pills and nav links

Key-value table

Emailmaya@example.com
Websitehttps://maya.example.com
LocationHackney, London
.table-hr, for details (accounts/_table.erb, organisations/_table.erb, events/_info_table.erb, activity and local group pages): labels bold in a column only as wide as the longest, rows ruled with a --bs-gray-200 line and no other borders

Striped table

Kitchen
2
Welcome desk
1
Friday
Saturday Sign up
Sunday Sign up
Grids of slots: rotas (rotas/_rota.erb), timetables and the admin dashboard; also some admin lists (members, applications, teams, mailers) · stripes --bs-tertiary-bg · each cell an avatar-sm or a btn-sm to sign up · roles and slots carry their points as text-bg-yellow badges

Tooltips

hover these
data-bs-toggle="tooltip" and a title: app.js has one handler for the whole page, so content loaded later needs no setup. HTML is allowed, and with no title it shows the hidden span after the element (reaction names, timetable descriptions) · .875rem, --bs-emphasis-color at 90%, radius base, max 12.5rem wide · label icons, truncated text and disabled controls, not essential information

Progress and loading

60/80
Places filled (events/_progress.erb, _ticket_types_progress.erb), pay and rota progress · --theme-500 on --theme-200 · the count as a link above, or inside the bar where it's full width
.bi-spin turns any icon (an order in progress) · Pace, on every page load and ajax: a 2px --gradient-active bar along the top, and a black spinner at --sticky-top on the right · .placeholder, a faint grey line that pagelets show while they load

Charts

Chart.js, with white bold value labels from chartjs-plugin-datalabels.
Bar (_chart.erb): monthly counts in --theme-500, with the current month's projection stacked on top in --theme-300 (make_projection estimates it from the days so far; here it's fixed)
Doughnut (_stats.erb): chart_colors turns the theme's hue evenly round the wheel at oklch lightness .71, chroma .11, so every segment has the same weight

Tabs and pills

.nav · .875rem · hover and focus: dark text on --bs-secondary-bg, no focus ring · active pill --theme-500 · .tab-content padding 1rem · in .nav-wrapper, tabs scroll sideways on mobile

Modal

37.5rem wide from 768px · no border or shadow

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.
.search-input: up to 32.5rem, form-control-lg, the clear button a 1.5rem --bs-gray-400 circle, --bs-gray-600 on hover · .search-tab: bleeds to the edges of #content, a --bs-gray-400 rule below; tabs weight 600 in --bs-secondary-color, the active one --bs-emphasis-color with a 1px underline of the same, which hover previews. It's the only underlined tab style: .nav-tabs and .nav-pills are for everything else · .search-result-list: each result base top and bottom with a 2px --bs-border-color rule below, the image floated in a 10rem column (5.625rem on mobile), the name an h2

Header search and autocomplete

  • Breathwork Collective
  • Breath and sound journey
  • Holotropic breathwork
Search: offcanvas-top, 2.8125rem, --bs-gray-800, backdrop at .9 · autocomplete (here and on the messages page): flush under its input, so square at the top with the base radius below, and while it's open (.autocomplete-open) the input's bottom corners are square, with no bottom border or focus ring, like Select2, --hairline border, --bs-box-shadow, items --space-2xs across --space-base, hover --bs-tertiary-bg like dropdown items

Notifications and messages dropdowns

.notification-list: list-group-flush, items --space-sm across --space-base, smaller newsfeed icons · .message-preview: unread --theme-100, hover --bs-tertiary-bg like the notification items

Messages

  • Are you coming to the full moon gathering?
  • Yes! Can I bring a friend?
  • Of course, see you there 🌕
  • Read 2 minutes ago
messages.css, loaded only by /messages: their bubbles --bs-gray-200 with --bs-body-color text, yours --theme-500 with white text and underlined links, each --bs-border-radius-xl round with a tail at the bottom corner, on the left for theirs and the right for yours · their avatar a 1.5rem circle beside the bubble's tail · Read and when, --font-size-xs in --bs-secondary-color, under your last message · the reply box is the ask box; Enter sends, Shift+Enter adds a line

Mobile bottom sheet

Below 768px, nav crumbs (dropdown_nav, ul_nav), the notifications and messages dropdowns open in #nav-dropdown-sheet (layouts/application.erb) in place of a dropdown: an offcanvas-bottom as tall as its content up to 80dvh, its top corners --bs-border-radius-xl · app.js copies the menu in as a list-group-flush with no borders, headers as .dropdown-header with no line, as in nav crumbs, dividers as an hr · the event stats row opens in it too, as a list of its columns

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 from rake 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).
Oct 5 Mon 5 Oct Location Link
Site: .event-detail.calendar-date, .event-detail.location (or the camera for online events) and .event-detail.link · hover one
Default theme
Own theme colour
Email: 160px PNGs in app/assets/images/icons/calendar/png and icons/image, picked by emails/_tickets_table.erb

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.

Breathwork and sound journey

Featured Sold out
breathwork sound healing
Name h2 · labels: Featured text-bg-yellow, Sold out or Sales ended text-bg-danger, Locked text-bg-secondary · tags text-bg-primary, up to 5, with bi-stars when AI-tagged · .icon-list at fs-4, each line's icon hung 2em to the left, with a tooltip naming it

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.
Sat 4 Oct
Sun 5 Oct Sold out
Starts Fri 10 Oct
Wed 15 Oct
On-demand
Arrows: 2.5rem white circles with black bi-chevron-compact icons, the whole button at 50% opacity, 75% on hover · dates and Sold out as .badge-overlay (see Badges) · before Slick loads, only the first item shows

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
£
Ticket groups: a .ticket-group-heading row, a bold 1.25rem heading, its group's first divider a 2px rule in --bs-emphasis-color · each type: the name bold at --font-size-base (.ticket-type-name), a lock icon if it's secret or for members, "3 left" or fewer as a text-bg-secondary badge · price at fs-4: fixed, a form-range for a range (the price shows once dragged), or a £ input group for pay what you like, which enables the quantity · quantity a full-width form-select, 0 to 99, or to the number left if fewer · the description in --bs-secondary-color, its paragraphs without margin · sold out: Join waitlist (btn-sm btn-outline-primary), which opens the waitlist modal (purchase/_ticket_type_waitlist_modal.erb), or a Sold out badge in text-bg-danger, as in event blocks, the carousel and the places-filled bar · a ticket type whose sales have ended says Sales closed in text-bg-secondary · the right-hand column: Apply a code, a membership discount, the donation (1/5/10% of the total, 1/2.5/5% on large orders, or a set amount for the organisation) and the disabled Total, Credit, Discount and Balance fields from currency_input_row, each a bold label over a 5em input group · below 576px the card's sides narrow to .75rem, cells take table-sm's .5rem with none at the table's edges, and the range hint says just Drag, so the three columns fit a phone

Comments

Lift shares from London

Maya 2 hours ago
I'm driving down from Hackney on Friday morning, room for three. Message me!
Jonas 1 hour ago
Count me in, thank you @Maya
A post (comments/_post.erb) is a card with a text-bg-primary header, its subject · each comment: an avatar-xs, the name bold, the time in --bs-secondary-color, the body linkified · reactions bordered tiles, your own bordered --theme-500, with the reactors' names in a tooltip

Avatars

xs
sm
md · private
lg
xl
Square (accounts/_square.erb), except the 1.75rem header avatar and the 1.5rem chat avatars, which are round: the account's thumbnail or Gravatar, with their name in a tooltip · a size class, from 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 white

Ratings and feedback

4.8/5 from 36 ratings across 12 events
A beautiful, gentle evening. Maya held the space with such care.
Symbiota replied

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

.average-rating (event_feedbacks/_average_rating.erb): the average at --font-size-2xl, the count in --bs-secondary-color, a star per point · public answers beside a bi-quote, at .readable · .read-more (app.js) cuts a reply at its first line break and adds a Read more link at --font-size-xs, which shows the rest · the feedback form (event_feedbacks/build.erb, styled by event_feedbacks.css): five 2.75rem stars on a --bs-tertiary-bg panel, radius lg, --bs-gray-300 until chosen, then --color-orange-400 with .chosen

Image wall

.image-wall: groups of people as avatars that wrap, --space-2xs between rows: who chose an option or is interested in an activity, team and gathering members, attendees, followers · on options, teams and rotas, a "Show names instead of pictures" link reloads them as names separated by middots

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.
Day numbers and headings --bs-secondary-color, since FullCalendar makes them href-less anchors that would otherwise take the link colour · today's number white on a --theme-500 circle, with no day tint · toolbar buttons like .btn-secondary, the active view filled --theme-500 like the active pill · prev and next borderless · on mobile the title drops to --font-size-base and Today hides

Timetable

A gathering's activities (timetables/_tactivity.erb), dragged into a table-striped grid of times by spaces · 8rem tall, 16rem wide in the grid · white with a 2px --bs-body-color border and text · .tactivity-image takes the image from --tactivity-image, darkened by black at 50% under white text

Map markers

People
Gatherings
Events
Organisations
8
cluster
42
cluster
130
cluster
Google Maps (map.js): map-icons pins in --theme-500 for people and gatherings, --color-red-400 for events and organisations, each with its white Bootstrap icon · clusters --theme-500, --color-orange-500, then --color-red-500 as they grow, each with a 40% halo, drawn as SVGs since MarkerClusterer takes an image, with white Plus Jakarta Sans counts · areas drawn in --color-orange-400, filled at 35%

Map info window

Maya

Hackney, London

Clicking a marker loads /points/:model/:id into Google's InfoWindow, wrapped in .infowindow: a person (points/_account.erb, as here), an organisation or gathering, or an event as its block's text without the image (events/_block_main.erb as ul.map-event, list items at --font-size-sm) · Google draws the white box, its tail and the close button; app.css sets its font to Plus Jakarta Sans, sizes the close button and zeroes .row margins inside

Email previews

.email-preview-iframe-container, on the ticket, reminder and feedback request email pages (events/ticket_email.erb and friends): the email rendered by its own route into a borderless iframe that fills the container, 80vh tall (20rem here)

Cookie banner

layouts/_cookie_consent.erb, fixed 1rem from the bottom, up to 64rem wide · radius lg, --hairline border, --bs-box-shadow · Necessary only and Customise until Customise is pressed, which shows the choices (as here, .form-check checkboxes) and Save choices · one column on mobile, the buttons stretched

Empty states

No upcoming events

A short "No …" phrase in --bs-secondary-color, left-aligned and without a full stop, where the list or table would be: grey, since it is the absence of content rather than content

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.
Suggested description
A gentle evening of breathwork and sound for all levels.
06

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.

DEFAULT ·
Book
Saved. Text is --theme-800
AI well: lavender
THEMED · #1a73e8
Book
Saved. Text is --theme-800
AI well: theme tint
THEMED · #ffc107 → #e6ac00 (clamped)
Book
Saved. Text is --theme-800
AI well: theme tint
Each themed panel sets the same variables on itself that /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.