bootstrap-component

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Bootstrap 6 component CSS

Bootstrap 6 组件CSS

Author a component's styles and markup — the non-JavaScript pieces. Bootstrap 6 components are built from a Sass map of CSS custom properties (the token map), a base class that emits those tokens, and modifier classes that override them.
For interactive behavior, see the
bootstrap-component-js
skill. For palettes, theme colors, and color modes, see the
bootstrap-color-system
skill.
编写组件的样式与标记——即非JavaScript部分。Bootstrap 6组件由包含CSS自定义属性的Sass映射(令牌映射)、输出这些令牌的基类,以及覆盖令牌的修饰符类构建而成。
如需交互行为,请参考
bootstrap-component-js
技能。如需调色板、主题色与颜色模式,请参考
bootstrap-color-system
技能。

Workflow

工作流程

  • Step 1: Plan the component
  • Step 2: Create the partial and token map
  • Step 3: Write the base class
  • Step 4: Add modifiers, variants, and sizes
  • Step 5: Wire up theme colors
  • Step 6: Markup and accessibility
  • Step 7: Register the partial
  • Step 8: Document the component
  • Step 9: Verify

  • 步骤1:规划组件
  • 步骤2:创建分部文件与令牌映射
  • 步骤3:编写基类
  • 步骤4:添加修饰符、变体与尺寸
  • 步骤5:接入主题色
  • 步骤6:标记与可访问性
  • 步骤7:注册分部文件
  • 步骤8:编写组件文档
  • 步骤9:验证

Step 1: Plan the component

步骤1:规划组件

  1. Pick a singular, kebab-case name. It drives everything: the partial
    scss/_<name>.scss
    , the base class
    .<name>
    , the token prefix
    --<name>-*
    , and the map
    $<name>-tokens
    .
  2. Decide what the component needs before writing CSS:
    • Tokens only — one look, customized through CSS variables. Model on
      scss/_alert.scss
      .
    • Variants — contextual color treatments like
      .badge-subtle
      . Model on
      scss/_badge.scss
      .
    • Variants and sizes — model on
      scss/buttons/_button.scss
      .
  3. Check whether an existing component already covers the need. Extending one with a modifier is preferable to adding a near-duplicate.
Never edit generated output in
dist/
,
js/dist/
, or
_site/
. Source lives in
scss/
and
js/src/
.

  1. 选择一个单数形式的kebab-case命名。它将决定所有相关内容:分部文件
    scss/_<name>.scss
    、基类
    .<name>
    、令牌前缀
    --<name>-*
    ,以及映射
    $<name>-tokens
    。
  2. 在编写CSS前确定组件的需求:
    • 仅令牌 — 单一外观,通过CSS变量自定义。参考
      scss/_alert.scss
      的模式。
    • 变体 — 上下文相关的颜色处理,例如
      .badge-subtle
      。参考
      scss/_badge.scss
      的模式。
    • 变体与尺寸 — 参考
      scss/buttons/_button.scss
      的模式。
  3. 检查现有组件是否已满足需求。使用修饰符扩展现有组件,优于添加几乎重复的新组件。
切勿编辑
dist/
、
js/dist/
或
_site/
中的生成产物。源代码位于
scss/
和
js/src/
目录中。

Step 2: Create the partial and token map

步骤2:创建分部文件与令牌映射

Create
scss/_<name>.scss
. Declare the empty map first, then fill it through
defaults()
:
scss
@use "config" as *;
@use "functions" as *;
@use "mixins/border-radius" as *;
@use "mixins/tokens" as *;

$widget-tokens: () !default;

// scss-docs-start widget-tokens
// stylelint-disable-next-line scss/dollar-variable-default
$widget-tokens: defaults(
  (
    --widget-gap: var(--spacer-2),
    --widget-padding-x: var(--spacer-4),
    --widget-padding-y: var(--spacer-2),
    --widget-color: var(--theme-fg, inherit),
    --widget-bg: var(--theme-bg-subtle, var(--bg-1)),
    --widget-border-color: var(--theme-border, var(--border-color)),
    --widget-border-radius: var(--radius-5),
  ),
  $widget-tokens
);
// scss-docs-end widget-tokens
Rules that matter here:
  • The
    $widget-tokens: () !default;
    line is what makes the map overridable —
    @use "bootstrap/scss/bootstrap" with ($widget-tokens: (…))
    writes into it, and
    defaults()
    merges those overrides over your defaults. Passing
    null
    for a key removes that token entirely.
  • The second assignment needs the
    stylelint-disable-next-line scss/dollar-variable-default
    comment, since it intentionally reassigns without
    !default
    .
  • Token names are namespaced to the component and written unprefixed. Bootstrap's dist and CDN CSS run PostCSS to add the
    --bs-
    prefix (
    --bs-widget-bg
    ); compiling the source yourself keeps them unprefixed.
  • Build values from existing global tokens —
    var(--spacer-3)
    ,
    var(--radius-5)
    ,
    var(--border-width)
    ,
    var(--font-weight-semibold)
    — rather than hardcoded lengths, so the component scales with the rest of the framework.
  • The
    scss-docs-start
    /
    scss-docs-end
    markers are load-bearing: the docs page renders the map with
    <ScssDocs>
    , which throws at build time if the markers are missing.

创建
scss/_<name>.scss
文件。首先声明空映射,再通过
defaults()
填充:
scss
@use "config" as *;
@use "functions" as *;
@use "mixins/border-radius" as *;
@use "mixins/tokens" as *;

$widget-tokens: () !default;

// scss-docs-start widget-tokens
// stylelint-disable-next-line scss/dollar-variable-default
$widget-tokens: defaults(
  (
    --widget-gap: var(--spacer-2),
    --widget-padding-x: var(--spacer-4),
    --widget-padding-y: var(--spacer-2),
    --widget-color: var(--theme-fg, inherit),
    --widget-bg: var(--theme-bg-subtle, var(--bg-1)),
    --widget-border-color: var(--theme-border, var(--border-color)),
    --widget-border-radius: var(--radius-5),
  ),
  $widget-tokens
);
// scss-docs-end widget-tokens
此处的重要规则:
  • $widget-tokens: () !default;
    这一行是映射可被覆盖的关键——
    @use "bootstrap/scss/bootstrap" with ($widget-tokens: (…))
    可以向其中写入内容,
    defaults()
    会将这些覆盖值合并到你的默认值之上。为某个键传入
    null
    会完全移除该令牌。
  • 第二次赋值需要添加
    stylelint-disable-next-line scss/dollar-variable-default
    注释,因为它是有意不带
    !default
    的重新赋值。
  • 令牌名称带有组件命名空间,且不带前缀编写。Bootstrap的dist版本与CDN CSS会通过PostCSS添加
    --bs-
    前缀(即
    --bs-widget-bg
    );自行编译源码则会保留无前缀的形式。
  • 使用现有全局令牌构建值——例如
    var(--spacer-3)
    、
    var(--radius-5)
    、
    var(--border-width)
    、
    var(--font-weight-semibold)
    ——而非硬编码的长度值,这样组件可以随框架整体缩放。
  • scss-docs-start
    /
    scss-docs-end
    标记是必需的:文档页面使用
    <ScssDocs>
    渲染映射,如果缺少这些标记,构建时会报错。

Step 3: Write the base class

步骤3:编写基类

Declarations go inside a cascade layer, with the token map emitted on the root selector:
scss
@layer components {
  .widget {
    @include tokens($widget-tokens);

    display: flex;
    gap: var(--widget-gap);
    align-items: center;
    padding: var(--widget-padding-y) var(--widget-padding-x);
    color: var(--widget-color);
    background-color: var(--widget-bg);
    border: var(--border-width) solid var(--widget-border-color);
    @include border-radius(var(--widget-border-radius));
  }
}
  • Pick the right layer. Components use
    @layer components
    , form controls
    @layer forms
    , helpers
    @layer helpers
    . The order is declared once in
    scss/_root.scss
    :
    colors, config, root, reboot, layout, content, forms, components, custom, helpers, utilities
    .
  • @include tokens($widget-tokens)
    comes first, then declarations, then nested rules. Declarations read
    var(--widget-*)
    , never the Sass map directly.
  • Use
    @include border-radius()
    and
    @include transition()
    instead of the raw properties — stylelint blocks the raw ones because the mixins honor
    $enable-rounded
    ,
    $enable-transitions
    , and
    prefers-reduced-motion
    .
  • Use logical properties (
    padding-inline-start
    ,
    margin-block-end
    ,
    inset-inline-start
    ) so the component works in RTL without an override.
  • No
    lighten()
    /
    darken()
    — use
    color-mix()
    , relative
    oklch()
    , or
    light-dark()
    . No
    border: none
    /
    outline: none
    — use
    0
    .

声明需放在级联层内,令牌映射输出在根选择器上:
scss
@layer components {
  .widget {
    @include tokens($widget-tokens);

    display: flex;
    gap: var(--widget-gap);
    align-items: center;
    padding: var(--widget-padding-y) var(--widget-padding-x);
    color: var(--widget-color);
    background-color: var(--widget-bg);
    border: var(--border-width) solid var(--widget-border-color);
    @include border-radius(var(--widget-border-radius));
  }
}
  • 选择正确的层。组件使用
    @layer components
    ,表单控件使用
    @layer forms
    ,辅助类使用
    @layer helpers
    。层的顺序在
    scss/_root.scss
    中统一声明:
    colors, config, root, reboot, layout, content, forms, components, custom, helpers, utilities
    。
  • @include tokens($widget-tokens)
    放在最前面,然后是声明,最后是嵌套规则。声明通过
    var(--widget-*)
    读取值,切勿直接使用Sass映射。
  • 使用
    @include border-radius()
    和
    @include transition()
    而非原生属性——stylelint会禁止使用原生属性,因为这些mixin会遵循
    $enable-rounded
    、
    $enable-transitions
    配置以及
    prefers-reduced-motion
    设置。
  • 使用逻辑属性(
    padding-inline-start
    、
    margin-block-end
    、
    inset-inline-start
    ),这样组件无需额外覆盖即可支持RTL布局。
  • 不要使用
    lighten()
    /
    darken()
    —— 请使用
    color-mix()
    、相对
    oklch()
    或
    light-dark()
    。不要使用
    border: none
    /
    outline: none
    —— 请使用
    0
    。

Step 4: Add modifiers, variants, and sizes

步骤4:添加修饰符、变体与尺寸

A modifier only overrides tokens; it should not repeat the base declarations:
scss
.widget-lg {
  --widget-padding-x: var(--spacer-6);
  --widget-padding-y: var(--spacer-3);
  --widget-border-radius: var(--radius-7);
}
When a component has a set of contextual treatments, drive them from an overridable map whose values are theme sub-keys, then loop. This is the
scss/_badge.scss
pattern:
scss
// scss-docs-start widget-variants
$widget-variants: () !default;
$widget-variants: defaults(
  (
    "subtle": (
      "color": "fg",
      "bg": "bg-subtle",
      "border-color": "transparent"
    ),
    "outline": (
      "color": "fg",
      "bg": "transparent",
      "border-color": "border"
    )
  ),
  $widget-variants
);
// scss-docs-end widget-variants

@layer components {
  // scss-docs-start widget-variant-loop
  @each $variant, $properties in $widget-variants {
    .widget-#{$variant} {
      @each $property, $value in $properties {
        @if $value == "transparent" {
          --widget-#{$property}: transparent;
        } @else {
          --widget-#{$property}: var(--theme-#{$value});
        }
      }
    }
  }
  // scss-docs-end widget-variant-loop
}
Mapping to theme sub-keys rather than literal colors is what lets
.widget-outline.theme-success
work without a variant-per-color explosion.
Sizes follow the button pattern: a list run through
defaults()
, looped into classes that re-point the component's tokens at the shared
--btn-input-<size>-*
scale.
scss
$widget-sizes: () !default;
$widget-sizes: defaults(
  ("sm", "lg"),
  $widget-sizes
);
defaults()
turns a list into a map, so a project can drop a size with
$widget-sizes: ("sm": null)
or add its own.

修饰符仅覆盖令牌,不应重复基类的声明:
scss
.widget-lg {
  --widget-padding-x: var(--spacer-6);
  --widget-padding-y: var(--spacer-3);
  --widget-border-radius: var(--radius-7);
}
当组件有一组上下文相关的样式时,通过一个可覆盖的映射来驱动,映射的值为主题子键,然后通过循环生成样式。这是
scss/_badge.scss
的模式:
scss
// scss-docs-start widget-variants
$widget-variants: () !default;
$widget-variants: defaults(
  (
    "subtle": (
      "color": "fg",
      "bg": "bg-subtle",
      "border-color": "transparent"
    ),
    "outline": (
      "color": "fg",
      "bg": "transparent",
      "border-color": "border"
    )
  ),
  $widget-variants
);
// scss-docs-end widget-variants

@layer components {
  // scss-docs-start widget-variant-loop
  @each $variant, $properties in $widget-variants {
    .widget-#{$variant} {
      @each $property, $value in $properties {
        @if $value == "transparent" {
          --widget-#{$property}: transparent;
        } @else {
          --widget-#{$property}: var(--theme-#{$value});
        }
      }
    }
  }
  // scss-docs-end widget-variant-loop
}
映射到主题子键而非字面颜色,这样
.widget-outline.theme-success
才能正常工作,避免了为每种颜色都创建一个变体的爆炸式增长。
尺寸遵循按钮的模式:将一个列表通过
defaults()
处理,循环生成类,将组件的令牌重新指向共享的
--btn-input-<size>-*
尺度。
scss
$widget-sizes: () !default;
$widget-sizes: defaults(
  ("sm", "lg"),
  $widget-sizes
);
defaults()
会将列表转换为映射,因此项目可以通过
$widget-sizes: ("sm": null)
移除某个尺寸,或添加自定义尺寸。

Step 5: Wire up theme colors

步骤5:接入主题色

Theme classes (
.theme-primary
,
.theme-danger
, …) are generated from
$theme-colors
and set a fixed set of variables:
--theme-base
,
--theme-fg
,
--theme-fg-emphasis
,
--theme-bg
,
--theme-bg-subtle
,
--theme-bg-muted
,
--theme-border
,
--theme-focus-ring
, and
--theme-contrast
.
A component gets theme support for free by reading those variables with a neutral fallback, which is why the defaults in Step 2 look like this:
scss
--widget-color: var(--theme-fg, inherit),
--widget-bg: var(--theme-bg-subtle, var(--bg-1)),
--widget-border-color: var(--theme-border, var(--border-color)),
Unthemed markup uses the fallbacks; adding a theme class recolors the component with no extra CSS:
html
<div class="widget theme-danger">…</div>
Use
.theme-reset
on a nested subtree to drop inherited
--theme-*
tokens and return to those fallbacks.
Do not hardcode
var(--red-500)
or a specific theme color in component tokens. Adding or overriding theme colors themselves is covered by the
bootstrap-color-system
skill.

主题类(
.theme-primary
、
.theme-danger
等)由
$theme-colors
生成,会设置一组固定的变量:
--theme-base
、
--theme-fg
、
--theme-fg-emphasis
、
--theme-bg
、
--theme-bg-subtle
、
--theme-bg-muted
、
--theme-border
、
--theme-focus-ring
以及
--theme-contrast
。
组件通过读取这些变量并设置中性的回退值,即可免费获得主题支持,这也是步骤2中的默认值看起来像这样的原因:
scss
--widget-color: var(--theme-fg, inherit),
--widget-bg: var(--theme-bg-subtle, var(--bg-1)),
--widget-border-color: var(--theme-border, var(--border-color)),
无主题的标记会使用回退值;添加主题类即可重新着色组件,无需额外CSS:
html
<div class="widget theme-danger">…</div>
在嵌套的子树上使用
.theme-reset
,可以移除继承的
--theme-*
令牌,回退到上述回退值。
切勿在组件令牌中硬编码
var(--red-500)
或特定的主题色。添加或覆盖主题色本身的内容请参考
bootstrap-color-system
技能。

Step 6: Markup and accessibility

步骤6:标记与可访问性

  • Start from the semantic element. Add ARIA only where the markup can't convey meaning on its own — for example
    role="alert"
    on a live message,
    aria-expanded
    on a disclosure trigger.
  • Interactive parts must be reachable by keyboard and show a visible focus ring. The
    .focus-ring
    helper applies
    outline: var(--focus-ring-width) solid var(--theme-focus-ring, var(--focus-ring-color))
    on
    :focus-visible
    ; theme-aware components inherit the right color automatically.
  • Give icon-only controls an accessible name with
    .visually-hidden
    text or
    aria-label
    .
  • Never signal state through color alone; pair it with text, an icon, or an ARIA attribute.
  • Keep the markup RTL-safe: logical properties in CSS, no left/right assumptions in the DOM order.

  • 从语义化元素开始。仅在标记本身无法传达含义时添加ARIA——例如在实时消息上添加
    role="alert"
    ,在展开/收起触发器上添加
    aria-expanded
    。
  • 可交互部分必须可通过键盘访问,并显示可见的焦点环。
    .focus-ring
    辅助类会在
    :focus-visible
    状态下应用
    outline: var(--focus-ring-width) solid var(--theme-focus-ring, var(--focus-ring-color))
    ;支持主题的组件会自动继承正确的颜色。
  • 为仅含图标的控件提供可访问名称,可通过
    .visually-hidden
    文本或
    aria-label
    实现。
  • 切勿仅通过颜色传达状态;需搭配文本、图标或ARIA属性。
  • 保持标记的RTL安全性:CSS使用逻辑属性,DOM顺序中不要有左/右的固有假设。

Step 7: Register the partial

步骤7:注册分部文件

Forward the partial from
scss/bootstrap.scss
in the components block, keeping the existing ordering:
scss
@forward "widget";
Components that live in a subdirectory (
scss/buttons/
,
scss/forms/
,
scss/helpers/
) are forwarded from that directory's
index.scss
barrel, and the barrel is forwarded from
scss/bootstrap.scss
. Add new files to the barrel, not to
bootstrap.scss
, in that case.

在
scss/bootstrap.scss
的components块中转发该分部文件,保持现有的顺序:
scss
@forward "widget";
位于子目录(
scss/buttons/
、
scss/forms/
、
scss/helpers/
)中的组件,从该目录的
index.scss
barrel文件转发,barrel文件再由
scss/bootstrap.scss
转发。这种情况下,将新文件添加到barrel中,而非直接添加到
bootstrap.scss
。

Step 8: Document the component

步骤8:编写组件文档

  1. Add
    site/src/content/docs/components/<name>.mdx
    :
    markdown
    ---
    title: Widget
    description: Documentation and examples for Bootstrap’s widget component.
    toc: true
    css_layer: components
    ---
    Add
    js: optional
    (or
    js: required
    ) when the component ships a JavaScript plugin.
  2. Add an entry to
    site/data/sidebar.yml
    under the Components section. The sidebar drives the nav, the page ordering, and inclusion in
    llms.txt
    , so a page without an entry is effectively invisible:
    yaml
    - title: Widget
      meta:
        - added: 6.0.0
  3. Show usage with
    <Example>
    for live demos, and render the token map under a
    ### Variables
    section:
    markdown
    <CSSVariables component="Widgets" className="widget" />
    
    <ScssDocs name="widget-tokens" file="scss/_widget.scss" />
  4. Link internally with
    [[docsref:/components/widget/]]
    rather than a hardcoded versioned path.

  1. 添加
    site/src/content/docs/components/<name>.mdx
    文件:
    markdown
    ---
    title: Widget
    description: Documentation and examples for Bootstrap’s widget component.
    toc: true
    css_layer: components
    ---
    如果组件附带JavaScript插件,添加
    js: optional
    (或
    js: required
    )。
  2. 在
    site/data/sidebar.yml
    的Components部分添加条目。侧边栏决定了导航、页面排序以及是否包含在
    llms.txt
    中,因此没有条目的页面实际上是不可见的:
    yaml
    - title: Widget
      meta:
        - added: 6.0.0
  3. 使用
    <Example>
    展示用法以提供实时演示,并在
    ### 变量
    部分渲染令牌映射:
    markdown
    <CSSVariables component="Widgets" className="widget" />
    
    <ScssDocs name="widget-tokens" file="scss/_widget.scss" />
  4. 使用
    [[docsref:/components/widget/]]
    进行内部链接,而非硬编码的带版本路径。

Step 9: Verify

步骤9:验证

  1. npm run css-lint
    — catches disallowed properties, wrong declaration order, and missing logical properties.
  2. npm run css
    — compiles, prefixes, and minifies. Confirm the tokens land in
    dist/css/bootstrap.css
    with the
    --bs-
    prefix.
  3. npm run docs-build
    — fails on a broken
    docsref
    , a missing sidebar entry, or missing
    scss-docs
    markers.
  4. Check the component in the browser: with and without a theme class, in both color modes, with
    dir="rtl"
    on
    <html>
    (v6 has no separate RTL stylesheet — logical properties do the work), and with reduced motion enabled if it animates.

  1. npm run css-lint
    —— 检查出不允许的属性、错误的声明顺序以及缺失的逻辑属性。
  2. npm run css
    —— 编译、添加前缀并压缩。确认令牌以
    --bs-
    前缀出现在
    dist/css/bootstrap.css
    中。
  3. npm run docs-build
    —— 当
    docsref
    损坏、侧边栏条目缺失或
    scss-docs
    标记缺失时会构建失败。
  4. 在浏览器中检查组件:分别在有和无主题类的情况下、两种颜色模式下、
    <html>
    设置
    dir="rtl"
    的情况下(v6没有单独的RTL样式表——由逻辑属性实现),如果组件有动画,还要在启用减少动效的情况下检查。

In your own project

在你自己的项目中

The same pattern works for your own components on top of Bootstrap. In
custom.scss
,
@use
the pieces you need and follow Steps 2–6 unchanged:
scss
@use "bootstrap/scss/config" as *;
@use "bootstrap/scss/mixins/tokens" as *;
@use "bootstrap/scss/bootstrap";

$widget-tokens: () !default;
$widget-tokens: defaults(
  (
    --widget-bg: var(--theme-bg-subtle, var(--bg-1)),
  ),
  $widget-tokens
);

@layer custom {
  .widget {
    @include tokens($widget-tokens);
    background-color: var(--widget-bg);
  }
}
Use the
custom
layer, which sits after
components
and before
helpers
and
utilities
, so your styles win over component defaults while utilities still override yours. Retuning an existing Bootstrap component doesn't require any of this — override its CSS variables at runtime, or pass its
$*-tokens
map through
with ()
at compile time.
同样的模式也适用于你基于Bootstrap开发的自定义组件。在
custom.scss
中,
@use
你需要的部分,然后不变地遵循步骤2-6:
scss
@use "bootstrap/scss/config" as *;
@use "bootstrap/scss/mixins/tokens" as *;
@use "bootstrap/scss/bootstrap";

$widget-tokens: () !default;
$widget-tokens: defaults(
  (
    --widget-bg: var(--theme-bg-subtle, var(--bg-1)),
  ),
  $widget-tokens
);

@layer custom {
  .widget {
    @include tokens($widget-tokens);
    background-color: var(--widget-bg);
  }
}
使用
custom
层,它位于
components
之后、
helpers
和
utilities
之前,因此你的样式会覆盖组件默认值,而工具类仍能覆盖你的样式。调整现有Bootstrap组件不需要这些——在运行时覆盖其CSS变量,或在编译时通过
with ()
传入其
$*-tokens
映射即可。