- Absence is the default. A component receiving a minimal
$argsarray should always render sensibly on its own. Only pass what differs from the default. - Presence is an explicit override. If a key exists with a meaningful value, the component acts on it.
- Elvis sets the bridge. Use
get_arg() ?: 'default'when a fallback value is needed. Never passfalseor empty values to communicate "off" — just omit the key. - No boolean flags. Avoid passing
trueorfalseas option values. Use presence vs absence instead —is_arg()is your boolean check.
ORA Capital — Design System
Get Started
This design system is the shared language for the site. Every component, layout, colour, and rule lives here first. There are four schools of thought on how to use the system.
1. A rule-setting environment
This is where conventions are decided and written down — naming patterns, escaping rules, token scales, spacing units.
2. A pattern library
Before building something new, check here first. Established patterns — buttons, containers, layouts, typography — are documented with working markup so you can copy the example rather than re-solving a problem that's already been solved. If a pattern exists here, use it; don't roll your own variant in a one-off template.
3. A scratchpad for new components
The design system is also where new components get built and tested in isolation, away from live content and real templates. A section here is allowed to be unfinished while it's actively being worked on — but leave yourself a plain note at the top of the section saying so (e.g. "WIP — not finalised, layout still being tested") so nobody mistakes a half-built idea for an established pattern.
Remove the note once the component is settled and ready to be treated as a reliable pattern.
4. A stable fixture for visual regression testing
BackstopJS (and any other visual regression tooling) treats this page as a fixed reference — it screenshots what's here and diffs future builds against it. That only works if the content is static and predictable, which is why every section is built with hardcoded dummy data rather than live or dynamic content.
Two consequences follow from this:
Never wire a design system section to get_field(), get_posts(), or any other live query — that's what makes a screenshot comparison reliable in the first place.
Be aware: reordering sections, renaming a section's name slug, or changing markup that a screenshot baseline depends on will register as a visual diff, even if nothing is actually broken.
Bootstrap utility classes
Bootstrap's utility classes (.mx-auto, .d-flex, .text-center, and so on) are available and fine to reach for directly in markup for one-off layout tweaks — there's no need to write a bespoke class for something Bootstrap already solves in one place.
Never select a utility class from inside a component's own .scss file. A component should only ever style its own BEM classes. Utility classes belong in markup, not as selectors in component CSS — see copilot-instructions.md → "Don't target utility or typography classes as selectors in component CSS".
Breakpoint utilities specifically
Bootstrap's responsive suffixes (-sm, -md, -lg, -xl) are not a problem to use in markup, but they get hard to read once a chain of them builds up on one element — p-7 p-lg-9 px-lg-32 doesn't tell you much at a glance about what's actually changing or why.
Inside component or layout SCSS, prefer the hand-coded @include bp() mixin with the theme's named breakpoint variables instead of reaching for a Bootstrap breakpoint suffix:
@include bp($tablet) { padding: var(--spacer-9); }
The named variables ($tablet, $desktop, etc., defined in sass/variables-settings/_layout-settings.scss) read closer to plain English than lg/xl, and keep responsive logic in one legible place rather than spread across a string of utility classes.
Testing
The theme ships with two layers of automated testing: PHPUnit for PHP logic, and BackstopJS for visual regression. Neither requires a CI server to be useful — both run locally before you push.
Unit tests (PHPUnit)
Covers core theme functions that other code relies on — is_arg(), get_arg(), add_classes(), h_tag(), and breadcrumb helpers. Run the full suite with:
vendor/bin/phpunit
Tests live in /tests/Unit/. Requires composer install to have been run first, since PHPUnit and the WordPress stubs it type-checks against come from /vendor.
Add a test alongside the existing ones whenever you add a new template helper function — this is the layer that catches a logic regression before it ever reaches a browser.
Visual regression testing (BackstopJS)
BackstopJS screenshots a list of URLs at three viewports — phone, tablet, and desktop — and diffs new screenshots against an approved reference set. This design system page, and every other section on this page, is part of that default URL list — which is why design system sections must stay static rather than pulling from live content (see "Get Started" → A stable fixture for visual regression testing).
See the Visual Regression Testing section of the theme's README.md for first-time setup and the commands to run it.
What it won't catch
Real images are stubbed out with a static placeholder (backstop_data/engine_scripts/interceptImages.js) so screenshots stay deterministic between runs — lazy-loaded and randomly-selected media can't cause a false diff. That means a content or photo change won't register here. Layout, spacing, colour, and typography changes will.
Adding coverage
As real pages get built, add their paths to the items array in backstop-urls.json (not the local-only copy) so the whole team benefits from the same coverage. Design system sections are already listed via ?ds_page= query params — keep that in sync if a section's name slug ever changes.
Template Functions
Array argument validation is_arg() and get_arg()
Used to safely read values from $args arrays passed into template parts via get_template_part().
Principles
What counts as a value
| Value passed in | get_arg() returns |
Example |
|---|---|---|
null | false | get_arg( 'label', array( 'label' => null ) ) // false |
false | false | get_arg( 'label', array( 'label' => false ) ) // false |
'' | false | get_arg( 'label', array( 'label' => '' ) ) // false |
array() | false | get_arg( 'items', array( 'items' => array() ) ) // false |
| Key missing | false | get_arg( 'label', array() ) // false |
0 | 0 | get_arg( 'count', array( 'count' => 0 ) ) // 0 |
| Any string | The string | get_arg( 'label', array( 'label' => 'Submit' ) ) // 'Submit' |
| Any non-empty array | The array | get_arg( 'items', array( 'items' => array( 'a', 'b' ) ) ) // array( 'a', 'b' ) |
Template Part - section-zoo.php
/*------------------------------------*\
# EXAMPLE ARRAY
\*------------------------------------*/
$animal_args = array(
'animals' => array( 'dog', 'deer', 'cat' ),
'title' => get_field( 'title' ),
'sub_title' => 'Animals',
);
get_template_part( 'template-parts/component', 'animals', $animals_args );
Template Part - component-animals.php
/*------------------------------------*\
# EXAMPLE OUTPUT
\*------------------------------------*/
$is_title = is_arg( 'title', $args ); // This will return false because get_field('title') doesn't exist.
$is_nothing = is_arg( 'something', $args ); // This will return false.
$is_animals = is_arg( 'animals', $args ); // This will return truthy as an array.
$sub_title = get_arg( 'sub_title', $args ); // This will return 'Animals'.
$animals = get_arg( 'animals', $args );
if ( $animals && is_array( $animals ) ) {
foreach ( $animals as $animal ) {
echo $animal;
}
}
What returns false
Both functions treat the following as "not set" — get_arg() returns false and is_arg() returns false for all of these:
$args = array(
'label' => null, // not set
'label' => false, // not set — indistinguishable from missing
'label' => '', // not set — no content
'items' => array(), // not set — no content
);
The integer 0 is the exception — it is a real value and passes through.
Set defaults with Elvis
Because missing and empty values all return false, the Elvis operator is all you need for defaults.
$label = get_arg( 'label', $args ) ?: 'Read more';
$button_text = get_arg( 'button_text', $args ) ?: 'Submit';
$title_tag = get_arg( 'title_tag', $args ) ?: 'h2';
Use is_arg() for conditional blocks
Use is_arg() when you need to decide whether to render a block at all.
if ( is_arg( 'animals', $args ) ) {
$animals = get_arg( 'animals', $args );
foreach ( $animals as $animal ) {
echo esc_html( $animal );
}
}
Attribute output from associative array get_attributes()
This is used in component_button() to add more attributes that don't have specific arguments.
$attributes = array(
'data-url' => 'http://lateralaspect.com.au',
'data-id' => 'latasp',
);
get_attributes( $attributes );
// Returns: data-url="http://lateralaspect.com.au" data-id="latasp"
Heading hierarchy offset h_tag()
Returns the correct heading tag for a component based on the context it is placed in. Write headings as if the component stands alone — pass the base level from $args and every heading steps correctly.
Accepts both 'h2' and '2' as input for either argument. Clamps at h6.
Formula
output = natural_level + ( base_level - 1 )
h_tag( '2', 'h1' ) // h2 — no offset
h_tag( '2', 'h2' ) // h3 — shifted by 1
h_tag( '2', 'h4' ) // h5 — shifted by 3
h_tag( '3', 'h5' ) // h6 — clamped at h6
Component usage
/* = VIEW LOGIC
----------------------------------------------- */
$title_tag = get_arg( 'title_tag', $args ) ?: 'h2';
$h1 = h_tag( '1', $title_tag ); // Primary heading
$h2 = h_tag( '2', $title_tag ); // Secondary heading
/* = MARKUP
----------------------------------------------- */
?>
<div class="c-component">
<<?= $h1; ?> class="c-component__title">
<?= esc_html( $main_heading ); ?>
</<?= $h1; ?>>
<<?= $h2; ?> class="c-component__subtitle">
<?= esc_html( $sub_heading ); ?>
</<?= $h2; ?>>
</div>
Passing the base level in
// Caller sets the base heading level for the component.
// Default to 'h2' if not provided.
$component_args = array(
'title_tag' => 'h2', // or 'h3', 'h4', etc.
'heading' => 'Main Title',
);
get_template_part( 'template-parts/component', 'example', $component_args );
Handing off to a nested component
// Pass h_tag() result as the child's base level.
$child_args = array(
'title_tag' => h_tag( '2', $title_tag ), // Child starts one level down.
);
get_template_part( 'template-parts/component', 'child', $child_args );
Inline SVG output render_svg()
Outputs an SVG file inline from the theme's assets/img/ directory. Use this instead of <img> tags for icons so they can be coloured via currentColor and avoid an extra HTTP request. Pass the filename without the .svg extension.
// Basic usage — outputs the SVG inline
<?= render_svg( 'arrow' ); ?>
// Inside component_icon() — preferred for icons used in components/buttons
component_icon( 'arrow', 'icon--up icon--currentColor' );
component_icon( 'arrow', 'icon--mirror icon--currentColor' );
// Never hardcode the theme path — use get_stylesheet_directory_uri() if you must use <img>
<img src="<?= esc_url( get_stylesheet_directory_uri() ); ?>/assets/img/logo.png" alt="Logo">
SVG file preparation
- Convert strokes to fills in Illustrator: Object > Expand before exporting.
- Compress with SVGOMG after export.
- Always add
width=""andheight=""attributes matching theviewBoxvalues — e.g.viewBox="0 0 22 22" width="22" height="22". - Prefer
pathelements withfilloverline/strokeelements socurrentColorinheritance works correctly.
<!-- DO — fill-based path, currentColor-ready -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" width="22" height="22">
<path fill="#678884" d="M21,22c..."/>
</svg>
<!-- DON'T — stroke-based lines, won't inherit currentColor reliably -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.828 22.828">
<line stroke="#678884" stroke-width="2" .../>
</svg>
Add classes to existing class list add_classes()
This function merges new classes with existing classes, removing duplicates and handling both string and array inputs.
// Adding string classes to string classes
$existing_classes = 'c-component c-component--primary';
$new_classes = 'c-component--large is-active';
$result = add_classes( $new_classes, $existing_classes );
// Returns: 'c-component c-component--primary c-component--large is-active'
// Adding array classes to string classes
$existing_classes = 'c-component c-component--primary';
$new_classes = array( 'c-component--large', 'is-active' );
$result = add_classes( $new_classes, $existing_classes );
// Returns: 'c-component c-component--primary c-component--large is-active'
// Handles duplicates automatically
$existing_classes = 'c-component c-component--primary';
$new_classes = 'c-component c-component--secondary';
$result = add_classes( $new_classes, $existing_classes );
// Returns: 'c-component c-component--primary c-component--secondary' (duplicate 'c-component' removed)
Colors
Usage
- Apply in HTML using a class name
.color-white.bg-white - or in CSS with a CSS Variable
color: var(--color-white);
Typography
Display & Heading Styles
Display and heading classes use Newsreader 400. Sizes are mobile / desktop (1240px+). Click Copy on any snippet.
<h2 class="f-display-1">Display 1</h2>
<h2 class="f-display-2">Display 2</h2>
<h2 class="f-display-3">Display 3</h2>
<h2 class="f-display-4">H1</h2>
<h2 class="f-display-5">H2</h2>
<h2 class="f-display-6">H3</h2>
<h2 class="f-display-7">H4</h2>
<h2 class="f-display-8">H5</h2>
<h2 class="f-display-9">H6</h2>
Sub Heading
Arial 700, uppercase, tracked — for eyebrows / label text above a display heading.
<span class="f-subheading">Sub Heading label</span>
Body
Body classes use Arial 400, line-height 1.6.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.
<p class="f-body-1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.</p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.
<p class="f-body-2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.</p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.
<p class="f-body-3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.</p>
Do not add manually — component_button() applies this to .c-button__text / .c-link__text.
<p class="f-body-button">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.</p>
Label
For tags and meta labels (e.g. news insights category tag).
Tags & labels (p-xs · 12 px)
<span class="f-label">Tags & labels</span>
Icons
Usage
Render icons through component_icon() so the wrapper and aria-hidden stay consistent.
Pass a slug (with or without the icon- prefix). Use classes for colour — typically icon--currentColor.
// Standalone icon.
component_icon( 'arrow-ne', 'icon--currentColor' );
// Inside a button — component_button() adds .c-button__icon automatically.
component_button(
array(
'title' => 'Explore what we do',
'url' => '#',
),
'c-button-1',
'arrow-ne',
'icon--currentColor'
);
Header navbar: menu chevrons use inline SVG via ora_capital_navbar_icon( 'chevron-down' ) — not component_icon().
FAQ accordion chevrons are inline SVG in component-faqs.php.
ORA CTAs use dedicated arrow-ne / arrow-ne-footer slugs (not CSS rotation modifiers).
Loading methods
Production icons load from assets/img/inline-icon-{slug}.svg.php via component_icon().
Component inline icon (preferred)
component_icon( 'arrow-ne', 'icon--currentColor' );
Inline SVG template part (fallback)
get_template_part( 'assets/img/inline', 'icon-arrow-ne.svg' );
Icon catalogue
Five inline icons used across components, header, and footer. Click Copy on any snippet.
Light-background CTAs (hero primary, split-text, split-media, cta-a, bento, news), news card arrow, person modal LinkedIn, 404 back. Header client-login dropdown items.
component_icon( 'arrow-ne', 'icon--currentColor' );
Dark-background CTAs (hero secondary, footer LinkedIn). Header client login buttons (desktop, tablet, mobile). Different default rotation — see _button.scss.
component_icon( 'arrow-ne-footer', 'icon--currentColor' );
People card link indicator. Base horizontal arrow — use .icon--up, .icon--mirror for direction.
component_icon( 'arrow', 'icon--currentColor' );
Person modal email action (via component_button).
component_icon( 'email', 'icon--currentColor' );
Person modal dismiss control.
component_icon( 'close', 'icon--currentColor' );
Buttons
Usage
All site CTAs use component_button() with c-button-1.
Typography (f-body-button) and BEM elements (.c-button__text, .c-button__icon) are applied automatically.
Only one style variant exists: .c-button-1. Modifiers: .c-button--dark, .c-button--mirror.
Default — .c-button-1
Light-background CTAs: hero primary, cta-a, bento active card.
component_button(
array(
'title' => 'Explore what we do',
'url' => '#',
),
'c-button-1',
'arrow-ne',
'icon--currentColor'
);
Dark — .c-button-1.c-button--dark (arrow-ne-footer)
Dark sections: hero secondary, mobile client login.
component_button(
array(
'title' => 'Discover ORA',
'url' => '#',
),
'c-button-1 c-button--dark',
'arrow-ne-footer',
'icon--currentColor'
);
Dark — .c-button-1.c-button--dark (arrow-ne)
Dark or tinted sections: split-text, split-media, news-insights, bento inactive cards, person modal LinkedIn.
component_button(
array(
'title' => 'Discover ORA',
'url' => '#',
),
'c-button-1 c-button--dark',
'arrow-ne',
'icon--currentColor'
);
On dark — .c-button-1 (no --dark modifier)
Footer LinkedIn button and desktop/tablet client login. White label on dark background without inverting.
component_button(
array(
'title' => 'LinkedIn',
'url' => '#',
),
'c-button-1',
'arrow-ne-footer',
'icon--currentColor'
);
Mirror — .c-button-1.c-button--mirror
404 page back link only. Icon on the left.
component_button(
array(
'title' => 'Back to Home page',
'url' => '#',
),
'c-button-1 c-button--mirror',
'arrow-ne',
'icon--currentColor'
);
Dark + email icon
Person modal email action.
component_button(
array(
'title' => 'Email',
'url' => '#',
),
'c-button-1 c-button--dark',
'email',
'icon--currentColor'
);
Header — client login
Desktop navbar uses a <button> inside .c-button-group. Tablet uses .c-button-group--tablet.
<div class="c-button-group" id="desktop-cl">
<?php
component_button(
array(
'title' => 'Client Login',
),
'c-button-1',
'arrow-ne-footer',
'icon--currentColor',
array(
'id' => 'desktop-cl-btn',
'aria-haspopup' => 'true',
'aria-expanded' => 'false',
'aria-controls' => 'panel-cl',
),
'button'
);
?>
</div>
ACF link field
Pass an ACF link array directly as the first argument.
component_button( get_field( 'link' ), 'c-button-1', 'arrow-ne', 'icon--currentColor' );
Links
Usage
Inline text links use component-specific markup — not component_button().
Each pattern pairs a typography class (f-body-1 / f-body-2) with a component BEM class.
CTA links use component_button() — see the Buttons section.
Footer link — .c-site-footer__link
Site footer contact, legal, and back-to-top links. Arial 700 · text span + animated line.
<a href="#" class="f-body-2 c-site-footer__link">
<span class="c-site-footer__link-text">Contact us</span>
<span class="c-site-footer__link-line" aria-hidden="true"></span>
</a>
Split list nav — .c-split-list-a__link
Section navigation in split-list-a. Arial 700 · underline on hover via navbar-underline-after.
<a href="#" class="f-body-1 c-split-list-a__link">
<span class="c-split-list-a__link-label">Section link</span>
</a>
Split list nav (label only) — .c-split-list-a__link--text
Title-only nav item when no URL is set. Same typography as links; no hover underline or keyboard focus.
<span class="f-body-1 c-split-list-a__link c-split-list-a__link--text">
<span class="c-split-list-a__link-label">Section label</span>
</span>
Breadcrumb — .c-page-banner__bc-link
Page banner and 404 breadcrumbs. Same underline pattern as navbar links.
<nav class="c-page-banner__breadcrumbs" aria-label="Breadcrumb">
<a href="#" class="f-body-2 c-page-banner__bc-link">
<span class="c-page-banner__bc-link-label">Home</span>
</a>
<span class="c-page-banner__bc-sep" aria-hidden="true"></span>
<span class="f-body-2 c-page-banner__bc-current" aria-current="page">Current page</span>
</nav>
Navbar — .nav-link
Header primary navigation. Label span receives underline on hover.
<a href="#" class="nav-link" role="listitem">
<span class="nav-link-label">About</span>
</a>
Breadcrumbs
Default
Pass the array directly when you need full control.
A full breadcrumb trail with intermediate levels.
$demo_items_full = array(
ora_capital_breadcrumbs_home_item(),
array( 'title' => 'Blog', 'url' => home_url( '/blog/' ) ),
array( 'title' => 'Category Name', 'url' => home_url( '/blog/category/' ) ),
array( 'title' => 'Current Page', 'url' => '' ),
);
Short trail
Two-level — common on pages sitting directly under home.
$demo_items_short = array(
ora_capital_breadcrumbs_home_item(),
array( 'title' => 'Current Page', 'url' => '' ),
);
Helper functions
Each helper returns a ready-to-use $items array for component_breadcrumbs().
get_auto_breadcrumbs()
Auto-detects the page context and delegates to the correct helper. This is the recommended single call for most templates. It covers:
- Pages — walks the parent hierarchy
- Single posts and custom post types — appends the archive
- Taxonomy archives (category, tag, custom) — walks the term parent hierarchy
- Date archives — year → month → day chain
- Post type and posts page archives
- Search results — includes the query in the label
- 404 pages
- Front page — returns an empty array (no breadcrumbs rendered)
Yoast SEO is used as the data source when active, with each context's manual helper as a fallback.
<?php component_breadcrumbs( get_auto_breadcrumbs() ); ?>
Live output on this page:
get_page_breadcrumbs()
Walks up the page parent hierarchy. Use on Pages.
<?php component_breadcrumbs( get_page_breadcrumbs() ); ?>
Live output on this page (Design System has no parents, so only two items):
get_post_breadcrumbs()
Appends an archive item between Home and the current post. For the built-in post type it uses the Posts Page from Settings → Reading; for custom post types it uses the registered archive link.
<?php component_breadcrumbs( get_post_breadcrumbs() ); ?>
get_yoast_breadcrumbs()
Pulls Yoast SEO's breadcrumb data and maps it into the standard $items format — you get Yoast's logic (primary category, custom breadcrumb titles, complex hierarchies) rendered with your own markup and BEM classes. Returns false if Yoast is not active.
Recommended pattern — Yoast when available, manual fallback otherwise:
<?php
$items = get_yoast_breadcrumbs() ?: get_page_breadcrumbs();
component_breadcrumbs( $items );
?>
Live output on this page via Yoast:
Yoast breadcrumbs are not enabled. Enable them under Yoast SEO → Search Appearance → Breadcrumbs.
get_taxonomy_archive_breadcrumbs()
Builds a trail for category, tag, and custom taxonomy archive pages. Walks up the term parent hierarchy automatically.
<?php component_breadcrumbs( get_taxonomy_archive_breadcrumbs() ); ?>
get_date_archive_breadcrumbs()
Builds a trail for year, month, and day archive pages — only as deep as the current archive goes.
<?php component_breadcrumbs( get_date_archive_breadcrumbs() ); ?>
get_search_breadcrumbs()
Builds a trail for search results pages, including the current search query in the label.
<?php component_breadcrumbs( get_search_breadcrumbs() ); ?>
get_404_breadcrumbs()
Builds a two-item trail for 404 error pages.
<?php component_breadcrumbs( get_404_breadcrumbs() ); ?>
Space and Gaps
Spacers
Spacers are fixed and should be used for layouts: external spacing and gutters.
See $spacers in _layout-settings.scss
Gaps
Gaps are relative to font-size and are used with .l-stack
See $gaps in _layout-settings.scss
Basics
Boxes and Colors
Colors further down the chain take precedence otherwise they will inherit the parent color.
Boxes have padding on all sides and usually have different padding at different screen sizes. Inline Bootstrap style equivalent would be
class="p-16 p-lg-32 p-xl-64"
or whatever combination you might need.
Below are two example boxes but customise everything to suit the design you are working on in layout/_box.scss
Lorem Ipsum Dolar Sit Emit
Aenean lacinia bibendum nulla sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Maecenas faucibus mollis interdum. Donec sed odio dui.
Sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Maecenas faucibus mollis interdum. Donec sed odio.
Lorem Ipsum Dolar Sit Emit
Aenean lacinia bibendum nulla sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Maecenas faucibus mollis interdum. Donec sed odio dui.
Sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Maecenas faucibus mollis interdum. Donec sed odio.
Images
Image loaded by WordPress attachment ID.
<?php $image_args = array ( 'id' => 779, 'size' => 'medium', 'classes' => array ( 0 => 'first-class', 1 => 'second-class', ), ); ?>
Image loaded by URL.
<?php $image_args = array ( 'url' => 'https://picsum.photos/800/450', 'alt' => 'A placeholder image from Picsum Photos', ); ?>
ID and URL both provided — ID wins.
When a valid attachment ID is present it takes precedence over the URL.
<?php $image_args = array ( 'id' => 779, 'url' => 'https://picsum.photos/800/450', 'alt' => 'A placeholder image from Picsum Photos', 'size' => 'medium', ); ?>
Invalid ID with URL — URL used as source.
When the ID does not resolve to an attachment, the URL is used instead of triggering a fallback.
<?php $image_args = array ( 'id' => '0000', 'url' => 'https://picsum.photos/800/450', 'alt' => 'A placeholder image from Picsum Photos', 'size' => 'medium', ); ?>
Image that fails to load with no fallback, revealing the .bg-loading state.
<?php $image_args = array ( 'id' => '0000', 'size' => 'medium', ); ?>
Image that doesn't exist with fallback .c-no-image.
<?php $image_args = array ( 'id' => '0000', 'size' => 'medium', 'fallback' => true, 'fallback_type' => 'event', ); ?>
Containers
Containers use a max width. The wrapper around it is what will create the outside gutter; this helps to isolate max width and the gutter to avoid doing math.
Set the default max width using $container-max-widths in sass/variables-settings/_layout-settings.scss
Never put a background colour and a max-width on the same element. Background goes on the outer wrapper so it spans the full viewport width; the inner wrapper (.max-w-* + .mx-auto) restricts and centres the content. See copilot-instructions.md → "Containers and Full-Width Backgrounds" for the full pattern.
Containers with .l-container--{name}
Give every flexible-content section its own .l-container--{name} modifier (e.g. .l-container--hero, .l-container--split) even if it needs no special styling yet. Naming the section type means a specific section-to-section spacing relationship can be targeted later in _stack.scss with an adjacent-sibling selector, without retrofitting a class onto existing markup.
.l-container--hero — full width, flex-centred, responsive padding built in..l-container--full — same base class, padding removed via modifier..max-w-* + .mx-auto is the default inner wrapper. .container can be used instead when a section wants breakpoint-stepped width restriction (from $container-max-widths) rather than a single fixed value — .container's own padding-x is 0 in this theme, so it nests inside .l-container without doubling up on gutters.
.container as the inner wrapper — width restriction steps per breakpoint.Containers with .l-stack-containers
Containers with Layouts and .bg-color and .l-stack-containers
.max-w-640Layouts
Layouts are more than likely going to sit inside a .container.
These would ideally be written in CSS so that they can be tweaked easily but they can also be built using Bootstrap which is baked in.
Cluster
The Cluster explained on Every Layout.
Split
Generally these stack on mobile but you could make a variation if you'd like.
This currently doesn't work with the Boostrap .gap- utility.
Lorem Ipsum Dolar Sit Emit
Aenean lacinia bibendum nulla sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Maecenas faucibus mollis interdum. Donec sed odio dui.
Sed consectetur. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Maecenas faucibus mollis interdum. Donec sed odio.
Videos
Streaming Video
Components
Three page compositions using the components built for flexible layouts. Each block uses representative static $args — not live ACF data.
Composition 1 — Home
Hero Banner · Split Text A · Bento CTA A · Split List A · Split Accordion A · Split Media A
Our capabilities
Expertise across the full advisory spectrum
What sets ORA apart
Understated. Highly Capable.
ORA is deliberately discreet in profile and formidable in capability — focused on outcomes, not noise.
Long-term partners.
We build enduring relationships with clients whose decisions span decades, not reporting quarters.
Clarity under complexity.
When structures, markets, and family dynamics collide, we bring calm judgement and a clear path forward.
Composition 2 — Inner page
Page Banner · Text A · Text List A · FAQs · Boast Cards · People · Testimonial · CTA A · Split Media A
Our Story
ORA was founded on a simple belief: sophisticated capital deserves equally sophisticated counsel — delivered with discretion and care.
Today we partner with families and businesses across Australia who expect clarity, judgement, and a long view.
Portfolio Management
Actively managing your investments to maximise growth and preserve your wealth across changing market cycles.
Wealth Structuring
Designing ownership and succession structures that protect assets while remaining flexible for the next generation.
Corporate Advisory
Strategic guidance for capital raising, transactions, and growth decisions that shape long-term enterprise value.
Family Office Services
Coordinated advisory for complex family groups — investments, governance, philanthropy, and intergenerational planning.
Frequently Asked Questions
Becoming a client starts with a confidential conversation about your goals, circumstances, and the outcomes that matter most to you.
We typically partner with individuals, families, and businesses navigating meaningful complexity — whether that is wealth transition, growth capital, or multi-entity structures.
Yes. While we are based in Perth, we advise clients across Australia and maintain the relationships and networks needed to support that work.
Corporate Success stories
Who we've helped
$9,000,000
Equity Capital Raising
ORA acted as:
Lead Manager & Bookrunner
$14,500,000
Placement
ORA acted as:
Joint Lead Manager
Our Team
Joel Ridley
Managing Director
Joel brings over two decades of experience advising families and businesses on complex capital decisions.
Sarah Chen
Director, Private Advisory
Sarah specialises in multi-generational wealth planning and the governance structures that support it.
James Okonkwo
Director, Corporate Advisory
James leads capital raising and transaction work for ambitious mid-market and listed businesses.
Emma Walsh
Associate Director
Emma works across private and corporate mandates, bringing analytical depth and client-first delivery.
Testimonial
ORA brought a level of clarity and care to our financial strategy that we hadn't experienced before.
Composition 3 — Capability page
Page Banner · Split List A · Text List A · Testimonial · Split Media A · Split Contact
Our capabilities
Expertise across the full advisory spectrum
Portfolio Management
Actively managing your investments to maximise growth and preserve your wealth across changing market cycles.
Wealth Structuring
Designing ownership and succession structures that protect assets while remaining flexible for the next generation.
Corporate Advisory
Strategic guidance for capital raising, transactions, and growth decisions that shape long-term enterprise value.
Family Office Services
Coordinated advisory for complex family groups — investments, governance, philanthropy, and intergenerational planning.
Testimonial
They understood the transaction and the people behind it — rare in this market.
Contact Form
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
Oops! We could not locate your form.
Bootstrap 5.2.3 Kitchen Sink
If this looks absolutely butchered GOOD! Not all modules are on and not all should be. Check the items that you're using and ignore the rest.
Contents
Typography
DocumentationDisplay 1
Display 2
Display 3
Display 4
Display 5
Display 6
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
This is a lead paragraph. It stands out from regular paragraphs.
You can use the mark tag to highlight text.
This line of text is meant to be treated as deleted text.
This line of text is meant to be treated as no longer accurate.
This line of text is meant to be treated as an addition to the document.
This line of text will render as underlined.
This line of text is meant to be treated as fine print.
This line rendered as bold text.
This line rendered as italicized text.
A well-known quote, contained in a blockquote element.
- This is a list.
- It appears completely unstyled.
- Structurally, it's still a list.
- However, this style only applies to immediate child elements.
- Nested lists:
- are unaffected by this style
- will still show a bullet
- and have appropriate left margin
- This may still come in handy in some situations.
- This is a list item.
- And another one.
- But they're displayed inline.
Images
DocumentationTables
Documentation| # | First | Last | Handle |
|---|---|---|---|
| 1 | Mark | Otto | @mdo |
| 2 | Jacob | Thornton | @fat |
| 3 | Larry the Bird | ||
| # | First | Last | Handle |
|---|---|---|---|
| 1 | Mark | Otto | @mdo |
| 2 | Jacob | Thornton | @fat |
| 3 | Larry the Bird | ||
| Class | Heading | Heading |
|---|---|---|
| Default | Cell | Cell |
| Primary | Cell | Cell |
| Secondary | Cell | Cell |
| Success | Cell | Cell |
| Danger | Cell | Cell |
| Warning | Cell | Cell |
| Info | Cell | Cell |
| Light | Cell | Cell |
| Dark | Cell | Cell |
| # | First | Last | Handle |
|---|---|---|---|
| 1 | Mark | Otto | @mdo |
| 2 | Jacob | Thornton | @fat |
| 3 | Larry the Bird | ||
Figures
DocumentationForms
Overview
DocumentationDisabled forms
DocumentationSizing
DocumentationInput group
DocumentationFloating labels
DocumentationValidation
DocumentationComponents
Accordion
Documentation.accordion-body, though the transition
does limit overflow. .accordion-body, though the transition
does limit overflow. .accordion-body, though the transition
does limit overflow. Alerts
DocumentationWell done!
Aww yeah, you successfully read this important alert message. This example text is going to run a bit longer so that you can see how spacing within an alert works with this kind of content.
Whenever you need to, be sure to use margin utilities to keep things nice and tidy.
Badge
DocumentationExample heading New
Example heading New
Example heading New
Example heading New
Example heading New
Example heading New
Example heading New
Example heading New
Breadcrumb
DocumentationButtons
DocumentationButton group
DocumentationCard
DocumentationCard title
Some quick example text to build on the card title and make up the bulk of the card's content.
Go somewhereCard title
Some quick example text to build on the card title and make up the bulk of the card's content.
Go somewhereCard title
Some quick example text to build on the card title and make up the bulk of the card's content.
- An item
- A second item
- A third item
Card title
This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.
Last updated 3 mins ago
Carousel
DocumentationDropdowns
DocumentationList group
Documentation- A disabled item
- A second item
- A third item
- A fourth item
- And a fifth one
- An item
- A second item
- A third item
- A fourth item
- And a fifth one
Modal
DocumentationNavs
DocumentationNavbar
DocumentationPagination
DocumentationPopovers
DocumentationProgress
DocumentationScrollspy
DocumentationFirst heading
This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.
Second heading
This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.
Third heading
This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.
Fourth heading
This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.
Fifth heading
This is some placeholder content for the scrollspy page. Note that as you scroll down the page, the appropriate navigation link is highlighted. It's repeated throughout the component example. We keep adding some more example copy here to emphasize the scrolling and highlighting.