core-scss
GitHub指导 Tabler 框架核心 SCSS 代码的编写规范,涵盖文件结构、组件模式、CSS 自定义属性管理及构建流程。
Trigger Scenarios
Install
npx skills add tabler/tabler --skill core-scss -g -y
SKILL.md
Frontmatter
{
"name": "core-scss",
"description": "Write or change styles in `core\/scss\/` — the framework itself. Use whenever a component needs a new class, modifier, size or colour variant, when a Sass or CSS custom property is added or renamed, when dark mode or RTL behaviour is involved, and before touching `_variables.scss`, `_props.scss` or anything under `core\/scss\/ui\/`. Covers where a style goes, the custom-property pattern and its build-time `--tblr-` prefix, dark mode, RTL, the docs markers, the SCSS unit tests and the lint\/size gates."
}
Styles in core/scss
core/scss is the product: a class that ships here is public API for every Tabler user. Components in shared/ui only assemble the class names defined here.
1. Where a style goes
| Path | Holds |
|---|---|
ui/_*.scss |
components — .badge, .card, .steps, one file per component |
layout/_*.scss |
page chrome — root, core, navbar, page, footer, dark, animations, accessibility |
utils/_*.scss |
utility classes — colors, background, text, sizing, shadow, opacity, scroll, hover |
mixins/, helpers/ |
mixins, functions, the utilities API |
bootstrap/ |
Tabler's managed copies of Bootstrap's own partials |
vendor/ |
overrides for third-party plugin CSS |
tests/ |
sass-true unit tests (see section 7) |
Entry points: tabler.scss (which forwards _core.scss, then _extends.scss last), plus the standalone bundles tabler-flags, tabler-marketing, tabler-payments, tabler-props, tabler-socials, tabler-themes, tabler-vendors. A new partial is not compiled until it is @forwarded from _core.scss (or the bundle it belongs to).
The module graph uses @use / @forward: a partial starts with @use '../config' as *, which is the hub forwarding settings, variables, variables-dark, maps, mixins and utilities. Cross-module @extend rules must stay in _extends.scss, which loads last.
2. The component pattern
@use '../config' as *;
.badge {
--badge-padding-x: #{$badge-padding-x};
--badge-font-size: #{$badge-font-size};
--badge-line-height: 1;
display: inline-flex;
padding: var(--badge-padding-y) var(--badge-padding-x);
font-size: var(--badge-font-size);
@include border-radius(var(--badge-border-radius));
}
- Every themeable value becomes a custom property declared at the top of the component's root rule, seeded from a Sass variable (
#{$badge-font-size}). Declarations below readvar(--badge-*), never the Sass variable directly — that is what lets users retheme without recompiling. - A value with no reason to be overridden can be a literal (
--badge-line-height: 1). - Modifiers set custom properties rather than redeclaring properties:
.badge-sm { --badge-font-size: … }. - Sass variables go to
_variables.scsswith!default, dark-mode counterparts to_variables-dark.scss.
3. Custom properties are authored bare
Write --badge-bg, not --tblr-badge-bg. The public --tblr- prefix is added at build time by .build/css-var-prefix.ts (a postcss pass in build-css.ts).
The consequence to remember: names owned by third-party libraries must not be prefixed. cssVarIgnore lists them (--bs-, --fc-, --gl-, --plyr-, --ts-, …). Prefixing one detaches the theming with no error anywhere — the library keeps reading its own name and simply never sees the value. When a vendor override introduces a new foreign name, add it to cssVarIgnore; core/scss/tests/css-var-prefix.test.mjs snapshots every custom property of tabler-vendors.scss, so a missing entry shows up as a --tblr--prefixed foreign name in the snapshot diff.
The snapshot moves in the other direction too. A vendor override that starts reading a Tabler name (var(--success), var(--form-valid-border-color)) adds a correctly prefixed --tblr-* entry, and the test fails until the snapshot is refreshed. Read the diff: every new name is one Tabler owns → pnpm --dir core exec vitest run --config vitest.scss.config.mjs -u and commit the .snap; a name a library owns → cssVarIgnore, never -u.
Global properties (--dir, colours, fonts, spacing) live in _props.scss, which emits them on :root, :host.
4. Dark mode
- Colour pairs are expressed with
light-dark()where possible (_variables.scss,layout/_root.scss), so one declaration covers both modes. - What cannot be expressed that way goes to
_variables-dark.scss, or tolayout/_dark.scssfor the visibility helpers. - Dark mode is keyed on
.theme-dark,[data-bs-theme='dark']and[data-theme='dark']— match all three when you add a selector, and keep the whole block behind@if $enable-dark-mode.
5. RTL
RTL stylesheets are generated by rtlcss in build-css.ts (--rtl), so do not hand-write RTL overrides. Two rules:
- Prefer logical properties (
padding-inline-start,inset-inline-end) — rtlcss then needs no help. - A physical transform that must flip uses the
--dirmultiplier (translateX(calc(var(--dir) * -50%))), and the declaration is marked/* rtl:ignore */so rtlcss does not negate an already-correct calc. Both patterns are in_utilities.scssandui/_steps.scss.
6. Docs markers
Snippets shown on documentation pages are pulled from the source with markers, so the docs cannot drift:
// scss-docs-start alert-variables
$alert-padding-y: … !default;
// scss-docs-end alert-variables
docs/components/CodeDocs.astro and docs/lib/llms.ts read these. When you rename or move a marked block, check who references the marker name before deleting it.
7. Unit tests (sass-true)
core/scss/tests/*.test.scss are real unit tests over mixins and functions, auto-discovered by core/scss/tests/scss.test.mjs and run through vitest:
pnpm --filter @tabler/core test:scss
pnpm --filter @tabler/core test # js + scss
Add a test when you write a mixin whose output is easy to break silently — the _cards.test.scss case (a 0% that must keep its unit or the whole color-mix() drops) is the model. Note that stylelint deliberately ignores core/scss/tests/**: autofix there would rewrite the assertions.
8. Gates
pnpm --filter @tabler/core lint:scss # stylelint (twbs config; prettier owns formatting)
pnpm run lint:scss # + find-unused-sass-variables (`lint:scss:vars` in core)
pnpm run check:tokens # shared/lib/tokens.ts must match the Sass maps
pnpm run lint:prettier # formatting
pnpm run bundlewatch # size budgets (tabler.css 80 kB, tabler.min.css 75 kB)
- A new entry in
$theme-colors,$avatar-sizesand friends must be regenerated intoshared/lib/tokens.tswithpnpm run generate:tokens— the check gate fails otherwise. - An unused Sass variable fails
lint:scss; delete it or use it. - Growth past a bundlewatch limit is a decision, not an accident: raise the number in
core/package.jsondeliberately and say so in the PR.
9. Checklist
- Partial in the right directory and
@forwarded from_core.scssor its bundle - Themeable values as custom properties at the top of the root rule, seeded from
!defaultSass variables - Custom properties written bare; new foreign names added to
cssVarIgnore - Dark mode via
light-dark()or all three dark selectors, behind$enable-dark-mode - Logical properties, or
--dir+/* rtl:ignore */; no hand-written RTL - No class, custom property, Sass variable or mixin parameter removed or renamed without an alias (
backward-compat) - sass-true test for a mixin that can break silently
-
lint:scss,check:tokens,lint:prettier,test:scssclean -
check:compatclean after a core build, with no new line in.build/compat-baseline.txt - Docs page and class table updated (
write-docs,class-reference), changeset written
Version History
- 340f719 Current 2026-09-22 21:47


