RTL
Right-to-left support in Chassis CSS — required HTML attributes, the RTL build, customizing values per direction, and serving LTR and RTL together.
Chassis CSS ships a right-to-left build alongside the default LTR build. The RTL output is generated from the same source by RTLCSS, which flips horizontal-direction-aware properties (margins, paddings, positioning) at compile time.
RTL support is still experimental and will evolve based on user feedback. Spotted something or have an improvement to suggest? Open an issue.
If you'll be working on RTL extensively, the RTLCSS documentation explains the directives that drive the conversion.
Required HTML
Two attributes on the <html> element are required for an RTL Chassis page:
dir="rtl"— tells the browser the document direction.lang="<lang-code>"— for examplelang="ar",lang="he",lang="fa". Required for accessibility and for browser hyphenation/word-break behaviour.
Then load the RTL build of Chassis CSS instead of the default. After installing @chassis-ui/css, the file is at dist/css/chassis.rtl.min.css:
<link rel="stylesheet" href="node_modules/@chassis-ui/css/dist/css/chassis.rtl.min.css">
Starter template
<!doctype html>
<html lang="ar" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="node_modules/@chassis-ui/css/dist/css/chassis.rtl.min.css">
<title>مرحبًا بالعالم!</title>
</head>
<body>
<h1>مرحبًا بالعالم!</h1>
<!-- Optional: bundle includes Popper -->
<script src="node_modules/@chassis-ui/css/dist/js/chassis.bundle.min.js"></script>
</body>
</html>
Approach
Chassis CSS's RTL support rests on two decisions:
- RTLCSS does the heavy lifting. Building two versions of Chassis from one source codebase keeps both in sync as the framework evolves. RTLCSS is also expressive — its value directives and string maps cover the cases where mechanical flipping isn't enough.
- Logical properties wherever possible. Chassis utilities favour logical naming over physical naming —
.ms-medium(margin start) instead of.ml-medium(margin left). Logical properties are direction-agnostic at the source level, so the same class name produces the right rendering whether the page is LTR or RTL. No build-time flipping is needed for them at all.
In practice, day-to-day work in Chassis isn't very different from default LTR — the framework handles the direction concern for you.
Customizing from source
If you compile Chassis from source, the same Sass variable, map, and mixin overrides documented under Customize → Sass apply to RTL builds. The build pipeline runs RTLCSS over the compiled CSS, so any override you make is picked up automatically.
Direction-specific values
RTLCSS value directives let a single Sass variable produce different values for LTR and RTL. Use /* rtl:{value} */ after the LTR value:
$font-weight-bold: 700 #{/* rtl:600 */} !default;
The compiled output:
/* chassis.css */
dt { font-weight: 700 /* rtl:600 */; }
/* chassis.rtl.css */
dt { font-weight: 600; }
Direction-specific font stacks
Not every typeface supports every script. To use a different font family in RTL, insert into the LTR stack with /* rtl:insert:{value} */:
$font-family-sans-serif:
Helvetica Neue #{"/* rtl:insert:Arabic */"},
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Arial,
"Noto Sans",
sans-serif !default;
In LTR the stack starts with Helvetica Neue; in RTL it starts with Helvetica Neue Arabic and falls back to the same chain.
LTR and RTL on the same page
To render both directions in a single page, RTLCSS string maps let you wrap each direction's stylesheet in a class selector and rename matching strings between LTR and RTL builds:
/* rtl:begin:options: {
"autoRename": true,
"stringMap":[ {
"name": "ltr-rtl",
"priority": 100,
"search": ["ltr"],
"replace": ["rtl"],
"options": {
"scope": "*",
"ignoreCase": false
}
} ]
} */
.ltr {
@import "@chassis-ui/css/scss/chassis";
}
/*rtl:end:options*/
After running Sass and then RTLCSS, every selector in the LTR build is prefixed with .ltr and every selector in the RTL build with .rtl. Apply the corresponding wrapper class to a region of your markup and that region renders in the chosen direction.
Edge cases worth knowing about:
- When switching
.ltrand.rtlregions, set thedirandlangattributes on the relevant ancestor accordingly — visual flipping alone isn't enough for assistive technology. - Loading both stylesheets is a real bandwidth and parse-time cost. See Customize → Optimize and consider loading the secondary direction asynchronously.
- Nesting Chassis inside a wrapper class can interfere with mixins that build descendant-selector trees. The known case is
form-validation-state(); track the upstream issue if you hit a related problem.
For projects that need both directions in a single stylesheet rather than two, PostCSS RTLCSS extends the RTLCSS approach into a PostCSS plugin that emits prefixed rules for both directions in one file.
The breadcrumb separator
The breadcrumb component is the only place in Chassis that needs a separate Sass variable for its RTL form: $breadcrumb-divider-flipped defaults to $breadcrumb-divider. Override either one independently as needed.