@canonical/react-ds-app-landscape
v0.45.0
Published
Landscape-specific components for the Pragma design system
Maintainers
Keywords
Readme
@canonical/react-ds-app-landscape
Landscape-specific components for the Pragma design system. This package provides specialized UI elements for the Landscape application tier.
Prerequisites
- React 19 or higher
Installation
bun add @canonical/react-ds-app-landscape @canonical/stylesImport the design system's styles first in your application's entry, before anything that imports a component:
import "@canonical/styles";Or, from a stylesheet: @import url("@canonical/styles");.
Then mark your root — <html class="app comfortable"> — as the @canonical/styles README describes. That one import is also what puts this package's stylesheets in a defined order against the rest of the design system's: see Every component stylesheet is in ds.components.apps-landscape below.
Each component brings its own stylesheet when you import it.
Usage
import { Button } from "@canonical/react-ds-app-landscape";
function AppHeader() {
return (
<header>
<Button>Settings</Button>
</header>
);
}Styling
Every component stylesheet is in ds.components.apps-landscape
Every component stylesheet under src/lib is wrapped in one cascade layer:
@layer ds.components.apps-landscape {
.ds.button {
/* … */
}
}The name comes from the tier this package implements. Component layers follow the design system's tier tree, one flat name per tier: ds.components.global for the components every application gets, ds.components.apps for the ones Canonical applications share, and ds.components.apps-landscape for this package, which specialises them. @canonical/styles names the tiers one level up, so a name like this one is appended after them and sorts above the tier it specialises.
src/lib/index.css names the layer as its first rule and then imports every component stylesheet:
@layer ds.components.apps-landscape;
@import url("./Button/styles.css");A layer takes its place in the order the first time its name is seen, so naming it there — before any component sheet can open a block with it — keeps that place the same whichever sheet a bundler emits first. Importing a component already brings its own stylesheet, so that file is for a consumer who wants every component's CSS in one import.
.storybook/styles.css is deliberately not wrapped: it is the Storybook harness rather than a component stylesheet, it is not published, and it carries no component rules.
Three things follow.
@canonical/react-ds-global and @canonical/react-ds-app are not wrapped yet — at the time of writing neither package's stylesheets carry a layer at all — so today they still beat this package for the opposite reason: unlayered beats layered, whatever the emit order. What the layer guarantees, once those packages land, is that this package's .ds.button beats the .ds.button in @canonical/react-ds-global by cascade layer instead of by whichever bundle a loader emitted last. Where the two rules have the same selector at the same specificity, the emit order used to decide; where the global tier reaches for a longer selector, as its density seat does at (0,3,0) against (0,2,0), specificity did.
The order needs @canonical/styles on the page, needs its statement first, and needs that statement to name the shared application tier, since this package's name is appended after the names already fixed. Measured in Chromium 151: with the statement as it stands today, a rule here beats the same rule in ds.components.apps when the shared package is emitted first and loses when it is emitted second; with the tiers named at the second level, which a change released alongside this one adds, it wins in both orders. Nothing under src/lib imports @canonical/styles — an application imports it once, as the installation instructions above say.
An application's own unlayered CSS now beats every rule in this package, whatever the selectors on either side, because an unlayered author rule outranks every layered one. That is CSS working as designed, and it is the deliberate escape hatch: an application that needs to override a component writes a plain rule and it wins. An application that does not want to win by accident puts its CSS in @layer app.
Rule for contributors: every component stylesheet under src/lib opens with that wrapper, src/lib/index.css names the layer and imports the new sheet. @keyframes and a component's own :root token defaults go inside the wrapper; @property and @font-face registrations stay outside, above the block, because no layer sorts a registration. Never reach for !important to win a fight — an important declaration inverts the layer order, so it cannot be arbitrated by layers at all. The "Cascade Layers" section of the @canonical/styles README is the reference for the full order and for what is deliberately left unlayered.
Storybook
cd packages/react/ds-app-landscape
bun run storybookComponent Specifications
Component specifications are defined in the Design System Ontology.
