Component Library 2.13.1 (configured with Dummy App)

Previews

      • Button
      • Hyper Link
      • Icon
        • Combobox
        • Email
        • Radio
        • Select
        • Telephone
        • Text
        • Textarea
      • Navigation Banner
      • Paginator
      • Article
        • Default
        • Hero Only
        • Multi Panel
      • Article Listing
      • Carousel
      • Fault Tolerance
        • Default
        • Mobile Accordion
      • Header
        • Alignment
        • Background Hero With Breadcrumbs
        • Default
        • Alignment
        • Default
      • Article Summary Card
        • Default
        • Error
        • Filled
        • No Legend
        • Broken Embed
        • Default
      • Image
      • List
        • Default
        • Quote On Surface
        • With Iframe Media
        • With Media Fit
        • Default
        • Quote On Surface
  • Style Guide

No matching results.

Pages

  • Developer Guide
  • Overview
  • Colours
  • Typography
  • Spacing
  • New Components
  • Interactive Behaviour
  • Component Variants
  • CSS Utilities
  • Extending Components
  • CSS Boundaries

No matching results.

Overview

GLL Component Library

A hierarchical design system built with ViewComponent for composing pages from reusable panels and patterns.

Component Hierarchy

Documents Page, Article — top-level document shells
Panels Hero, Carousel, Pattern, Breadcrumb — full-width page sections
Patterns MediaCard, TextCard, Image, Video — reusable content blocks
Components Button, HyperLink, Icon — atomic UI elements

How Composition Works

Documents compose panels via slots. Each panel type maps to a component class in PANEL_COMPONENTS.

Panels compose patterns via slots. The PatternComponent renders any pattern type dynamically using the Patterns registry.

Patterns compose components (buttons, links, icons) via slots.

The Panel Header Block

Every content panel offers the same optional heading block — title, subtitle and body — rendered by Components::PanelHeaderComponent. A panel declares it with renders_panel_header :title, :subtitle, :body (name a reader to map it, body: :introduction; give a block to build the component yourself, as the pattern panel does for a caller-supplied header). That registers the panel_header slot; the document sets the heading level by position (the first panel gets the h1) and default_level: covers a panel rendered on its own. A panel with no heading declares nothing and has no slot. The block renders .panel__header (title + subtitle) and .panel__body (rich text), and nothing at all when every field is blank. The block owns its own appearance in components/_panel-header.scss: centred by default, with variants a panel declares on the component rather than restyles from its own selector. align: :left left-aligns the header (article pages, the hero), body_align: :left left-aligns the rich-text body under a centred heading (pattern panels), and variant: :banner is the hero's looser stack with a body-weight subtitle. Pass them to renders_panel_header or to PanelHeaderComponent.new; an unknown value raises.

Media Fills the Box It Is Given

The same rule sized: a container owns the box (width, height, aspect, position) and declares how the media should fill it; the image, video or embed fills whatever box it gets and never takes a size from outside. ImageComponent, VideoComponent and EmbeddedCodeComponent take fit: and emit data-fit, which their own stylesheets implement: :auto (intrinsic, never upscaled), :contain (inside the box, centred), :cover (fill and crop), and for images :overlay (cover from outside the flow on desktop, so a portrait source cannot prop up a row, BOU-2514). The media card places its media in .media-card__media, sizes that box per size, alignment and layout, and declares the fit; the hero, carousel, pattern panel and footer do the same for the boxes they place media in. Patterns.cell_options(type) is what a grid declares on an image it places in a cell. No panel, page or card file sets a property on img, video or iframe. Decided under BOU-2903.

The Media Card Composes a Text Card

A media card is a text card plus media. MediaCardComponent renders its text half through renders_one :text_card, a real Patterns::TextCardComponent with layout_style: "nested", and the same composition rule applies between the two patterns as between a panel and its cards: the media card places the text card and publishes context to it; it never styles the text card's internals. The nested variant in patterns/_text-cards.scss owns the nested type steps, stack and spacing. Anything the media card needs from its text half is declared on that variant, the same mechanism as the panel header's align: and variant:: leading: :fluid (the plain layout's unitless title leading), meta: (a small line after the body, the article card's date) and title_link: (the title becomes the card's link and the card declares it). Never selected from the media card's own file. The box the text card lands in (flex sizing, the padding of a media-card__content wrapper) is the media card's; what is inside the box is the text card's.

Decided under BOU-2904 (2026-09-17) over the alternative of the media card rendering media-card__title / __body / __link itself, as its quote layout does. Composition won because the Ruby already models it that way, the nested variant already exists to receive the rules, the quote branch shows the cost of the alternative (a second implementation of title size, heading level, rich-text body and clickable dispatch, plus its own cta slot), and host apps have coupled to .media-card .text-card. BOU-2909 moved the remaining .text-card rules out of _media-cards.scss: the media card now selects only the media-card__content box it places (or a text card a host places directly), and ArticleSummaryCardComponent renders the nested text card instead of hand-writing its markup.

Key Conventions

  • Panel components use panel: as the standard kwarg
  • Pattern components use pattern: as the standard kwarg
  • All slot population happens in templates via with_* methods — never in before_render
  • Slot names reflect design intent, not component type (e.g. cta not button)
Developer Guide Colour System