Armature API Reference

Your complete reference for making layouts work on phones, tablets, laptops, and desktop iMacs. Everything here is designed so you can change your whole site's look in one place — no code editing needed.

1. Page Shells

Layout Containers

Sections and containers manage vertical flow and width on the page. Use opero-section to add generous top and bottom spacing between major page blocks so each chapter of the page has room to breathe. Use opero-container to keep the content centered and prevent text or grids from stretching too wide on large monitors.

Container Width Comparison
.opero-container--sm
.opero-container
.opero-container--lg
<!-- Major section with vertical rhythm -->
<section class="opero-section">
  <div class="opero-container">
    <h1 class="opero-display">Section Title</h1>
    <p>Your content here stays at a comfortable reading width.</p>
  </div>
</section>

<!-- Narrow container for focused content -->
<section class="opero-section">
  <div class="opero-container opero-container--sm">
    <h2>Focused Story</h2>
    <p>This container is narrower, so long paragraphs are easier to read.</p>
  </div>
</section>

<!-- Wide container for gallery grids -->
<section class="opero-section">
  <div class="opero-container opero-container--lg">
    <div class="opero-grid gap-6">
      <article class="opero-card span-12 md:span-6 lg:span-4">...</article>
      <article class="opero-card span-12 md:span-6 lg:span-4">...</article>
      <article class="opero-card span-12 md:span-6 lg:span-4">...</article>
    </div>
  </div>
</section>
ClassMax WidthBest For
.opero-container--sm860pxReading-focused pages (Bio, Statement)
.opero-container1280pxStandard pages (Gallery, CV)
.opero-container--lg1480pxWide editorial layouts
.opero-container--full100% viewportEdge-to-edge immersive views

Glass Box Cards

Cards are how you break up content into digestible chunks. The regular opero-card is a simple container with subtle shadow. The opero-spotlight card has a blurred background behind it, making it pop off the page. Use spotlight cards for featured work, highlighted testimonials, or anything you want to emphasize.

Regular Card

Clean container with consistent spacing. Use for portfolios, service descriptions, or any grouped content.

Spotlight Card

Blurred background draws attention. Perfect for featured work, testimonials, or call-outs you want to emphasize.

<!-- Simple card -->
<article class="opero-card p-6">
  <h3>Project Title</h3>
  <p>Description of your work.</p>
</article>

<!-- Spotlight card with background blur -->
<article class="opero-spotlight p-6">
  <h3>Featured Testimonial</h3>
  <p>"Your work changed how I see design."</p>
  <p class="opero-muted">— Client Name</p>
</article>
ComponentStylingBest For
.opero-cardWhite background, subtle shadowWork portfolios, testimonials, content blocks
.opero-spotlightBlurred background, elevated shadowFeatured work, highlight quotes, calls-to-action

Marketing Heroes

The foundation for every hero section and header on the page. Combine a kicker, a display heading, and a lead paragraph to form the top-of-page introduction. This three-layer hierarchy works for portfolio landing pages, exhibition introductions, and section openers.

Featured Exhibition

Translucence & Light

A year-long exploration of how layered materials create new visual languages when light passes through them. Featuring glass, resin, and cast acrylic works from the past decade.

<section class="opero-section">
  <div class="opero-container">
    <p class="opero-kicker">Highlighted Project</p>
    <h1 class="opero-display">Your Hero Title</h1>
    <p class="opero-lead">Your introductory paragraph explaining the context, mood, or story of this section.</p>
  </div>
</section>
ComponentPurposeScales Responsively
.opero-kickerEyebrow label above the headingYes
.opero-displayHero heading that carries the main messageYes
.opero-leadSubtitle or introduction paragraphYes

2. Grids & Structure

The Armature Grid

The grid system is explicit: opero-grid creates a 12-lane parent and each child uses span-* classes to declare its width. Combine responsive spans like span-12 md:span-6 lg:span-3 to reshape the same layout across devices.

A
B
C
D
E
F
G
H
<!-- 12-lane parent with responsive child spans -->
<div class="opero-grid gap-6">
  <div class="span-12 md:span-6 lg:span-3">A</div>
  <div class="span-12 md:span-6 lg:span-3">B</div>
  <div class="span-12 md:span-6 lg:span-3">C</div>
  <div class="span-12 md:span-6 lg:span-3">D</div>
</div>

<!-- Featured layout with explicit spans -->
<div class="opero-grid gap-4">
  <article class="span-12 md:span-8">Feature panel</article>
  <aside class="span-12 md:span-4">Context panel</aside>
</div>

<!-- Full-width child within the grid -->
<div class="opero-grid gap-4">
  <div class="span-full">Always full-width</div>
</div>
ClassCSS OutputConceptual Usage
.opero-gridrepeat(12, minmax(0, 1fr))Parent 12-lane layout track
.griddisplay: gridFreeform grid layouts without enforced 12 lanes
span-1span-12grid-column: span NDefine lane width per child
span-2span-fullgrid-column: span NFeature items, editorial callouts
md:span-* / lg:span-*Breakpoint lane overridesResponsive composition by viewport
gap-0gap-12gap: var(--space-X)Gutter control between grid cells

Breakpoints & Screen Sizes

Opero supports four screen sizes so your portfolio adapts from phones to large desktop monitors. sm is for large phones, md is for tablets, lg is for laptops, and xl is for desktop monitors. Use span prefixes like span-12 md:span-6 lg:span-3 xl:span-2 to reshape the same grid as the screen grows.

Item
Item
Item
Item

md: activates at 768 px · lg: activates at 1024 px

<div class="opero-grid gap-3">
  <div class="span-12 md:span-6 lg:span-3">Item</div>
  <div class="span-12 md:span-6 lg:span-3">Item</div>
  <div class="span-12 md:span-6 lg:span-3">Item</div>
  <div class="span-12 md:span-6 lg:span-3">Item</div>
</div>
ScreenWhen It ActivatesUse WhenExample Classes
sm: large phones640 px and widerEarly tablet-sized phones and compact landscapesm:d-block, sm:d-flex
md: tablets768 px and widerTablet and small laptop transitionsmd:span-6, md:d-block
lg: laptops1024 px and widerDesktop composition and multi-column layoutslg:span-3, lg:flex-row
xl: desktop monitors1280 px and widerLarge iMac and ultrawide refinementsxl:d-flex, xl:d-grid
(none) baseAll screensMobile-first lane defaultsspan-12, gap-4, p-4

Spacing & Visual Breathing Room

All the gaps, padding, and margins on your site follow one simple system. Change the spacing scale in the admin panel once, and every card, section, and gap updates instantly across your entire portfolio. Use classes like p-4 for interior padding, mb-6 for breathing room underneath a block, and gap-4 for consistent space between grid items.

Hover over the boxes to see padding and margin space

Padding Inspector: p-4

Interior Content

Margin Inspector: mb-6

Element with margin-bottom
<div class="p-2">Interior: var(--space-2) = 0.5 rem</div>
<div class="p-4">Interior: var(--space-4) = 1 rem</div>
<div class="mt-8">Top margin: var(--space-8) = 2 rem</div>
<div class="opero-stack gap-6">Gap: var(--space-6) = 1.5 rem</div>
Class PrefixOutput CSS VariableValueConceptual Usage
p-1padding: var(--space-1)0.25 remIcon gutters, tag inner padding
p-2padding: var(--space-2)0.5 remCompact controls, chip padding
p-4padding: var(--space-4)1 remCard bodies, form field groups
p-6padding: var(--space-6)1.5 remPanel interiors, modal padding
p-8padding: var(--space-8)2 remSpotlight blocks, hero sections
p-12padding: var(--space-12)3 remFull-bleed section padding
mt-* / mb-*margin-top / margin-bottomSame scaleVertical rhythm between components
gap-4gap: var(--space-4)1 remCSS Grid and Flex rhythm control

Masonry Pipeline

Masonry layout flows items into columns like a magazine layout. Taller items sit lower than short ones, filling space efficiently. Add filter buttons if you want visitors to click a category and narrow the view without leaving the page.

Painting one
Sculpture one
Painting two
Sculpture two
Painting three
<!-- Filter controls -->
<button data-armature-filter="*" aria-pressed="true">All</button>
<button data-armature-filter="painting" aria-pressed="false">Painting</button>
<button data-armature-filter="sculpture" aria-pressed="false">Sculpture</button>

<!-- Filterable masonry container -->
<div class="opero-masonry" data-armature-filterable>
  <article data-category="painting">...</article>
  <article data-category="sculpture">...</article>
  <!-- Multi-category: visible under both filters -->
  <article data-category="painting, sculpture">...</article>
</div>
Class / AttributeApplied ToFunction
.opero-masonryContainer elementActivates CSS column-count layout (1 → 2 → 3 → 4 at breakpoints)
data-armature-filterableContainer elementRegisters container with the JS filter engine observer
data-category="value"Each itemDeclares item category; comma-separated for multi-category items
data-armature-filter="*"Filter buttonWildcard — shows all items and resets active state
data-armature-filter="slug"Filter buttonShows only items whose data-category includes this slug
aria-pressedFilter buttonManaged automatically by armature.js for screen reader state

Visibility Forms

Show and hide elements at different screen sizes when a page needs a simpler mobile version or a richer desktop version. Hide something on all screens with d-none, then bring it back at a breakpoint like md:d-block when there is enough room.

Hidden on small screens · visible from md (768 px) upward
Always visible — no display modifier applied.
<!-- Show only at md and wider -->
<div class="d-none md:d-block">Visible from 768 px upward</div>

<!-- Show as flex only at lg and wider -->
<div class="d-none lg:d-flex">Visible from 1024 px upward as flex</div>

<!-- Hide at md and wider (mobile-only element) -->
<div class="md:d-none">Hidden from 768 px upward</div>
ClassCSS OutputEffective Range
d-nonedisplay: none !importantAll breakpoints
d-blockdisplay: block !importantAll breakpoints
d-flexdisplay: flex !importantAll breakpoints
d-griddisplay: grid !importantAll breakpoints
md:d-nonedisplay: none !important768 px and wider
md:d-blockdisplay: block !important768 px and wider
lg:d-nonedisplay: none !important1024 px and wider
lg:d-flexdisplay: flex !important1024 px and wider

3. Placing Content

Aspect Ratios & Fit

Aspect-ratio wrappers help your images mount cleanly inside the grid, even when the source files all have different dimensions. Pair them with object-cover when you want the frame filled, or object-contain when you want the full image visible.

square
video
portrait
landscape
golden
object-cover
Object cover sample
object-contain
Object contain sample
<!-- Aspect ratio containers — wrap the media element -->
<div class="aspect-square"><img src="..." class="object-cover" /></div>
<div class="aspect-video"><img src="..." class="object-cover" /></div>
<div class="aspect-portrait"><img src="..." class="object-cover" /></div>
<div class="aspect-landscape"><img src="..." class="object-cover" /></div>
<div class="aspect-golden"><img src="..." class="object-cover" /></div>

<!-- Object-fit applied directly to the media element -->
<img class="object-cover w-full h-full" src="..." alt="..." />
<img class="object-contain w-full h-full" src="..." alt="..." />
Classaspect-ratio ValueIdeal Usage
aspect-square1 / 1Avatar thumbnails, grid tile icons
aspect-video16 / 9Video embeds, cinematic stills
aspect-portrait3 / 4Work detail images, artist headshots
aspect-landscape4 / 3Exhibition photography, studio shots
aspect-golden1.618 / 1Curated editorial feature panels
object-coverobject-fit: coverFill frame completely; crops to fit
object-containobject-fit: containShow the entire image; letterboxes if needed

Magazine Typography

Armature gives you a ready-made hierarchy for headlines, intros, and editorial text. Use opero-display for the main statement, opero-lead for the opening paragraph, and drop caps or columns when you want a more magazine-like reading experience.

Kicker label — above the heading

Display Heading

This is an opero-lead paragraph. It uses a slightly larger font size and softer color to serve as a subtitle or introduction before body text begins.

Armature editorial columns let you flow long-form artist statements, biographical text, or press content into responsive two-column layouts without custom CSS. The drop cap anchors the first paragraph with editorial gravity and signals the start of a new section.

Subsequent paragraphs flow naturally in the column layout. Line height, measure, and rhythm are calibrated for comfortable reading at typical body sizes on screen.

Muted text for secondary metadata and auxiliary information.

Caption text for image labels, dates, and provenance notes.

<p class="opero-kicker">Section label</p>
<h1 class="opero-display">Display Heading</h1>
<p class="opero-lead">Introductory lead paragraph.</p>

<div class="md:columns-2">
  <p class="opero-drop-cap">First paragraph with drop cap anchor...</p>
  <p>Continuation flowing in column layout...</p>
</div>

<p class="opero-muted">Secondary metadata text.</p>
<p class="opero-caption">Image caption or provenance note.</p>

<!-- Rich text container for rendered Markdown output -->
<div class="opero-richtext">
  <h2>...</h2><p>...</p><blockquote>...</blockquote>
</div>
ClassFont TokenUsage
.opero-display--opero-font-displayHero headings, section titles, editorial anchors
.opero-kicker--opero-font-bodyCategory labels appearing above headings
.opero-lead--opero-font-bodySubtitle or intro paragraph below a heading
.opero-muted--opero-font-bodySecondary metadata, auxiliary copy
.opero-caption--opero-font-bodyImage captions, provenance notes, dates
.opero-drop-cap--opero-font-displayFirst paragraph of editorial blocks; large initial letter
.md:columns-2Two-column CSS flow for long-form text at ≥768 px
.opero-richtextboth tokensContainer for rendered Markdown or HTML content blocks

Timeline Lists

Auto-numbered lists are useful when the content has a sequence: a career timeline, a studio process, or the stages of an exhibition. Each item gets its number automatically and stays in order if you add or remove a step.

First milestone

Timeline lists auto-number starting from 01. Perfect for career arcs or step-by-step processes.

Second milestone

Each item is its own card with subtle styling and spacing rhythm built in.

Third milestone

The glass-frosted background adapts to your surface color from the design settings.

<div class="opero-list-group">
  <div class="opero-list-item">
    <h3>1987 – Born in Florence</h3>
    <p>Childhood years spent in Italian hill towns.</p>
  </div>
  <div class="opero-list-item">
    <h3>2010 – BFA from RISD</h3>
    <p>Focus on sculpture and spatial design.</p>
  </div>
  <div class="opero-list-item">
    <h3>2015 – First solo show</h3>
    <p>Brooklyn gallery, featuring site-specific installations.</p>
  </div>
</div>
ComponentElement(s)Purpose
.opero-list-groupContainerWrapper that manages list spacing and counter styles
.opero-list-itemItem (repeats)Individual timeline card with auto-numbering and glass background
counter(opero-step, decimal-leading-zero)Pseudo-elementDisplays 01, 02, 03 automatically as items are added

4. Interactive Details

Interactive Buttons

Buttons signal where the visitor should click next. The base opero-button is neutral. Add opero-button--primary when the action should stand out most, and opero-button--primary-alt when you want a softer secondary action.

<button class="opero-button">Default</button>
<button class="opero-button opero-button--primary">Primary action</button>
<button class="opero-button opero-button--primary-alt">Secondary option</button>

<!-- Links that look like buttons -->
<a href="/work" class="opero-button opero-button--primary">View My Work</a>
<a href="/about" class="opero-button opero-button--primary-alt">About Me</a>
ClassAppearanceWhen to Use
.opero-buttonNeutral surface buttonSecondary actions, navigation options
.opero-button--primaryYour brand color, bold presenceMain call-to-action
.opero-button--primary-altSoft brand color with light backgroundSupporting actions

Token Definitions

One place controls your entire visual identity. Change your primary color in the admin panel, and it updates every button, link, card, and decoration across your whole site instantly. If you want a one-off override, you can also use tokens directly inline on a single element.

--opero-primary
--opero-secondary
--opero-accent
--opero-background
--opero-surface
--opero-text
bg-surface
bg-alt
<div style="background: var(--opero-primary)">Primary</div>
<div style="color: var(--opero-text-soft)">Soft copy</div>
<div style="color: var(--opero-text-muted)">Muted copy</div>
<div style="background: var(--opero-primary); color: white; padding: 1rem; border-radius: 1rem;">Single element override</div>
TokenSource Key (admin config)Usage
--opero-primaryvisual.primaryColorBrand identity, buttons, active states
--opero-secondaryvisual.secondaryColorGradients, hover states, accents
--opero-accentvisual.accentColorHighlights, focus rings, chips
--opero-backgroundvisual.backgroundColorPage canvas behind all content
--opero-surfacevisual.surfaceColorCards, panels, modals sitting on canvas
--opero-textvisual.textColorPrimary reading text
--opero-text-softderivedSecondary copy, subheadings
--opero-text-mutedderivedCaptions, metadata, placeholders
--opero-font-bodyvisual.bodyFontFamilyParagraphs, UI labels, controls
--opero-font-displayvisual.headingFontFamilyHeadings, hero titles, display text
--opero-btn-radiusvisual.buttonRadiusBorder-radius applied to all buttons
--opero-panel-radiusvisual.panelRadiusCard and panel corner radius
--opero-input-radiusvisual.inputRadiusForm input corner radius
--opero-shadow-basevisual.shadowIntensityDefault component shadow
--opero-shadow-hovervisual.shadowIntensityElevated shadow on hover/focus

Magnetic Interaction

Hover over a magnetic button and your cursor pulls it slightly toward the center of the button. It is a premium micro-interaction that makes the page feel responsive and alive without changing the structure of the layout.

<button class="btn opero-magnetic">Studio</button>
<a class="btn opero-magnetic" href="/series">Series</a>
<button class="btn btn-dark opero-magnetic">Contact</button>
Class / BehaviourApplies ToDetails
.opero-magneticAny interactive elementActivates cursor-follow translate transform on mousemove
Proximity zoneAutomatic1.4× the element's largest dimension
Max displacementAutomaticCapped at ±10 px in both axes
ResetAutomatic on mouseleavetransform returns to translate(0, 0)
EasingCSS transition180 ms ease

Grid Filtering

Armature's filtering engine connects filter buttons to any data-armature-filterable container. Each button declares a category with data-armature-filter, and each item declares its categories with data-category. When a filter activates, non-matching items fade out and leave the layout.

Drawing
Photo
Drawing
Photo
<button class="btn" data-armature-filter="*" aria-pressed="true">All</button>
<button class="btn" data-armature-filter="photography" aria-pressed="false">Photography</button>
<button class="btn" data-armature-filter="drawing" aria-pressed="false">Drawing</button>

<div class="opero-grid gap-4" data-armature-filterable>
  <article class="span-12 md:span-6 lg:span-4" data-category="photography">...</article>
  <article class="span-12 md:span-6 lg:span-4" data-category="drawing">...</article>
  <!-- visible under both filters -->
  <article class="span-12 md:span-6 lg:span-4" data-category="photography, drawing">...</article>
</div>
AttributeElementFunction
data-armature-filter="*"ButtonWildcard: restores all items, resets filter state
data-armature-filter="slug"ButtonShows only items where data-category includes this slug
aria-pressedButtonManaged automatically by armature.js for accessibility
data-armature-filterableContainerOpts the container into filter engine observation
data-category="a, b"ItemComma-separated list of categories; item matches any in the list

Scroll Reveals

As visitors scroll down, cards and sections fade in and float up smoothly. By default everything appears at once, but you can stagger the reveals so cards come in one after another for a more dramatic entrance.

Reveal 01
Reveal 02
Reveal 03
<!-- Base reveal: triggers when 18% of element enters viewport -->
<article class="opero-reveal">...</article>

<!-- Staggered reveal sequence with incremental delays -->
<article class="opero-reveal" data-armature-delay="0ms">First</article>
<article class="opero-reveal" data-armature-delay="120ms">Second</article>
<article class="opero-reveal" data-armature-delay="240ms">Third</article>
Class / AttributeElementFunction
.opero-revealAny elementSets initial state: opacity 0 + translateY(40px); registers with observer
.is-revealedAdded by JSTriggers transition to opacity 1 + translateY(0)
data-armature-delay="Xms"Reveal elementSets transition-delay for staggered entrance sequences
ThresholdAutomatic18% of element must enter viewport before revealing
Root marginAutomatic−6% bottom offset — triggers slightly before element reaches bottom edge
FallbackAutomaticAll elements revealed immediately if IntersectionObserver unavailable