You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Gravity UI already exposes global semantic tokens and CSS custom properties for many
visual aspects of its components. This RFC proposes an additive extension of that public
CSS API for component parts and states that cannot currently be customized at the correct
scope.
A public theming API should let theme authors customize supported semantic parts by
declaring custom properties on public theme scopes. Consumer styles must not depend on
private .g-* elements, modifiers, pseudo-elements, DOM structure, or CSS implementation
details.
This proposal is deliberately non-breaking:
it does not change the default Gravity UI theme;
it does not change component DOM, public props, behavior, sizes, or state precedence;
every new variable is optional;
when no new variable is defined, the rendered and computed result must be the same as
before the change;
implementation details may be adjusted only to consume the new API without changing
their default result.
Scope
The proposal covers public typography and components exported by @gravity-ui/uikit and @gravity-ui/date-components: Text, Hotkey, Popup, legacy List, Menu, DropdownMenu,
SegmentedRadioGroup, Select, Switch, Calendar, Modal, Dialog, and Progress.
Breadcrumbs is included because it composes Popup, but it does not need a new variable. Its
menu popup must consume the existing Popup API.
Goals
Let custom themes configure the covered semantic parts without private Gravity UI
selectors.
Expose only stable, semantic customization points that represent real theme differences.
Reuse existing global tokens and component APIs instead of publishing aliases.
Keep structural layout, state composition, accessibility, and rendering strategy owned
by Gravity UI.
Preserve current size-specific, mode-specific, and state-specific Gravity UI defaults.
Support logical properties and RTL without publishing physical-direction APIs.
Allow the work to land component by component without requiring a major release.
Non-goals
Changing the appearance or behavior of an unthemed Gravity UI component.
Making private selectors, DOM nodes, pseudo-elements, or stacking strategy public.
Exposing every CSS declaration as a variable.
Adding a variable for a value already controlled by a suitable public semantic token.
Adding component aliases for variables owned by a nested public component.
API selection rules
A new variable belongs in this RFC only when all of the following are true:
The property needs to be customizable independently at component scope.
No existing public semantic token or component variable controls the value at the
required scope.
The value describes a stable visual part or state, rather than current markup or a CSS
technique.
Gravity UI can consume the variable while preserving its current result when the
variable is absent.
There are two narrow additions for API completeness: an opposite logical side may be
included when it is already independently meaningful in the component, and all members of
an accepted typography family may be included together. These additions cover the mirror
variables and the six Text letter-spacing groups below; they do not justify a general
Cartesian product of properties and states.
The following do not become new CSS API:
declarations that repeat a semantic token already consumed by Gravity UI;
defensive resets or implementation-only declarations;
variables that only rename an existing public customization point;
aliases for Popup, Menu, List, Text, or Button variables used by compound components;
implementation choices such as whether a surface is a border, pseudo-element, or child.
Logical and mirror variables
Opposite logical sides are both exposed when they are independently meaningful in the
existing component contract. This is why Menu group-divider padding and margin have both
block-start and block-end variables, and Select group-label padding has both inline-start
and inline-end variables.
Mirrors are not added mechanically. A shorthand already controls both sides for Menu item
padding and DropdownMenu separator margin. Select list margin is intentionally block-axis
only because this proposal does not introduce independent inline-axis spacing.
Compatibility contract
The CSS API is additive, but adding a declaration can still cause a visual regression if
its fallback does not reproduce the existing cascade. Implementations must therefore meet
all of these requirements.
Preserve the current default result
For a direct value, use the current value as the fallback:
For size-, mode-, or state-dependent values, keep a private fallback per existing branch:
.g-component_size_s {
--_--item-padding: <current s value>;
}
.g-component_size_m {
--_--item-padding: <current m value>;
}
.g-component__item {
padding: var(--g-component-item-padding, var(--_--item-padding));
}
A refactor is acceptable only if the computed default is equivalent for every supported
size, mode, state combination, LTR/RTL direction, and relevant input method.
Preserve the current cascade and state precedence
Gravity UI continues to decide which state wins. A public variable supplies a value; it
does not make consumer styles reproduce selectors.
Disabled remains stronger than hover, focus, pressed, checked, selected, active, and
danger.
Selected, active, danger, and ordinary interactive items retain their current relative
precedence unless a variable explicitly describes that exact state.
A component-owned foreground icon receives the corresponding semantic text color when
a color variable is set, without changing its default color when variables are absent.
prefers-reduced-motion continues to override configurable motion.
Preserve public structure and behavior
The implementation must not require:
a new DOM node or a changed DOM order;
a new public prop;
a renamed or newly documented private class;
different focus, pointer, keyboard, disabled, or loading behavior.
Internal selectors may of course be used inside Gravity UI to attach the public variables
to the right semantic parts. The prohibition on .g-* selectors applies to consumer
theme CSS, not to the library's own styles.
If an internal cleanup cannot preserve the default result, it must not be bundled into
this RFC. The implementation should either keep the existing internal rule or propose an
additional semantic CSS variable. It must not silently change Gravity UI defaults to make
customization simpler.
Solution proposal
Add 72 new variables and make legacy List consume one existing variable.
Component or area
New variables
Existing API reused or integrated
Text
6
Existing typography family, weight, size, height
Hotkey
5
Body typography tokens remain the fallbacks
Popup
1
Existing background, border, and radius API
Legacy List
2
--g-list-item-border-radius
Menu
12
Existing semantic color tokens
DropdownMenu
2
Menu and Popup API
SegmentedRadioGroup
21
Existing size geometry
Select
8
List and Popup API
Switch
6
Existing semantic colors and size geometry
Calendar
1
Popup API for calendar popups
Modal
1
Existing Modal radius and sizing API
Dialog
6
Existing Text and Button implementation
Progress
1
Existing Progress color API
Breadcrumbs
0
Existing Popup API
Total
72
Proposed CSS variables
The “current Gravity UI result” column is normative for compatibility, not a request to
replace the existing declarations with hard-coded values. The implementation may preserve
the existing cascade or use private fallbacks.
Text — 6
Variable
Applied to
Current Gravity UI result when unset
--g-text-caption-letter-spacing
All Text caption-* variants
normal
--g-text-body-letter-spacing
All Text body-* and body-short variants
normal
--g-text-code-letter-spacing
All Text code-* and code-inline-* variants
normal
--g-text-subheader-letter-spacing
All Text subheader-* variants
normal
--g-text-header-letter-spacing
All Text header-* variants
normal
--g-text-display-letter-spacing
All Text display-* variants
normal
All six groups are included so typography stays coherent and Themer can represent the
same property for every supported group.
Hotkey — 5
Variable
Applied to
Current Gravity UI result when unset
--g-hotkey-font-family
Hotkey content, including nested kbd elements
--g-text-body-font-family
--g-hotkey-font-weight
Hotkey content, including nested kbd elements
--g-text-body-font-weight
--g-hotkey-font-size
Hotkey content, including nested kbd elements
--g-text-body-1-font-size
--g-hotkey-line-height
Hotkey content, including nested kbd elements
--g-text-body-1-line-height
--g-hotkey-letter-spacing
Hotkey content, including nested kbd elements
normal
The five variables form a complete Hotkey typography contract while preserving body
typography as the default.
Popup — 1
Variable
Applied to
Current Gravity UI result when unset
--g-popup-box-shadow
Complete Popup shadow
Existing two-layer Popup shadow
Popup already exposes --g-popup-background-color, --g-popup-border-color, --g-popup-border-radius, and --g-popup-border-width. The shadow fallback must continue
to react to the resolved current border width and color exactly as it does today.
Legacy List — 2 new variables and one existing-variable integration
Variable
Applied to
Current Gravity UI result when unset
--g-list-padding
Padding of the items container
0
--g-list-item-gap
Block-axis space between adjacent non-virtualized items
0
Legacy List must also consume the existing --g-list-item-border-radius variable on its
item surface. This is an integration requirement, not a new variable.
--g-list-item-gap applies only to the non-virtualized flow. Its implementation must not
change virtualization measurements or introduce a new default layout model. Select may
map its own list values to the List it owns; consumer CSS must not select nested List
classes.
Menu — 12
Container, flow, and group dividers
Variable
Applied to
Current Gravity UI result when unset
--g-menu-padding
Menu container padding
Current size padding, --g-spacing-1 0
--g-menu-item-gap
Block-axis space between adjacent regular items
0
--g-menu-group-divider-padding-block-start
Content-side padding after a preceding divider
Current size-specific start padding
--g-menu-group-divider-padding-block-end
Content-side padding before a following divider
0 for s/m/l; current xl end padding
--g-menu-group-divider-margin-block-start
Outer margin before a group divider
Current size-specific start margin
--g-menu-group-divider-margin-block-end
Outer margin after a group divider
0 for s/m/l; current xl end margin
Gravity UI maps these variables to its current group markup. A theme does not need to know
which list item owns the border. Block-end variables are intentional mirrors: xl Menu
already has independently meaningful end spacing.
Item geometry and states
Variable
Applied to
Current Gravity UI result when unset
--g-menu-item-min-height
Minimum height of a regular Menu item
Current intrinsic size-specific height
--g-menu-item-padding
Padding of a regular Menu item
Current size-specific item padding
--g-menu-item-border-radius
Radius of a regular Menu item surface
Current square item surface
--g-menu-item-background-color-pressed
Pressed ordinary interactive item background
Existing ordinary interaction cascade
--g-menu-item-text-color-disabled
Disabled item text and owned foreground icon color
--g-color-text-secondary
--g-menu-item-background-color-danger-hover
Danger item hover and keyboard-focus background
Existing generic interactive hover background
The pressed variable is for an ordinary interactive item. It must not override the current
selected, active, danger, or disabled precedence. Menu-owned foreground icons may use currentColor or an equivalent internal mapping when a public text-color variable is set;
icon wrappers remain private.
Menu container text/background, divider color, normal item text, ordinary hover, selected,
active, and danger text continue to use global semantic tokens.
DropdownMenu — 2
Variable
Applied to
Current Gravity UI result when unset
--g-dropdown-menu-separator-margin
Logical margin around a group separator
0.5em 0
--g-dropdown-menu-separator-color
Group separator line color
--g-color-line-generic-solid
DropdownMenu already creates separators when the public items API contains multiple
item groups. The separator is a semantic DropdownMenu part.
separator-margin configures the space around the line; separator-color configures the
line color.
Regular adjacent DropdownMenu items use --g-menu-item-gap. Separator margin owns all
spacing around a separator, so Menu item gap must not be added on top of it.
Menu item geometry and state variables must apply to regular DropdownMenu items without
turning a separator into an item surface. The implementation should scope or map the new
Menu API to semantic regular items rather than changing the default separator layout.
DropdownMenu continues to reuse the Popup API for its root popup and submenus.
SegmentedRadioGroup — 21
Container and option
Variable
Applied to
Current Gravity UI result when unset
--g-segmented-radio-group-option-inset
Inset of an option surface
0
--g-segmented-radio-group-font-weight
Group font weight
--g-text-body-font-weight
--g-segmented-radio-group-background-color
Group background
Transparent
--g-segmented-radio-group-border-radius
Outer group radius
Current size-specific group contour
--g-segmented-radio-group-option-border-radius
Individual option-surface radius
Current outer-corner topology; inner corners remain square
Disabled values take precedence over checked values. The component remains responsible
for the current focus mechanism and for mapping the semantic surface variables to its
existing elements or pseudo-elements.
Under prefers-reduced-motion: reduce, Gravity UI continues to disable or minimize motion
regardless of the configured values.
Select — 8
Variable
Applied to
Current Gravity UI result when unset
--g-select-list-margin-block
Block margin of a non-mobile Select list in Popup
4px on both block sides
--g-select-list-padding
Padding of a non-mobile Select list container
0
--g-select-list-item-background-color-selected
Selected item background
Current single/multiple mode-specific backgrounds
--g-select-list-item-background-color-pressed
Pressed active option background
Existing selected/active interaction cascade
--g-select-list-option-padding-inline
Inline option padding
8px for s/m, 12px for l/xl
--g-select-list-option-text-color-disabled
Disabled option text and owned foreground icon color
Current label/content/icon colors
--g-select-list-group-label-padding-inline-start
Inline-start padding of a non-empty group label
Current size-specific start padding
--g-select-list-group-label-padding-inline-end
Inline-end padding of a non-empty group label
Current size-specific end padding
The variables apply to non-mobile Select. Mobile Select keeps its current layout and
styles. Selected fallback remains different for single and multiple modes. Pressed applies
only to an enabled option and takes precedence over selected while the option is actually
pressed; when the variable is absent, the current interaction result remains unchanged.
When disabled text color is defined, it reaches the default label, custom option content
through inheritance, and owned foreground icons, including the multiple-selection tick.
When it is not defined, their existing distinct colors remain intact.
The list keeps its normal available width after padding. Supporting the new padding must
not require consumer CSS to select a private list element or set its width.
Virtualized and non-virtualized lists retain their current overflow and measurement model.
Switch — 6
Variable
Applied to
Current Gravity UI result when unset
--g-switch-track-background-color-pressed
Unchecked pressed track background
Existing unchecked interaction cascade
--g-switch-track-background-color-checked-hover
Checked hover and pressed background
Current checked brand background
--g-switch-slider-background-color
Slider background
--g-color-base-background
--g-switch-slider-box-shadow
Slider shadow
none
--g-switch-track-box-shadow-checked
Checked track shadow
none
--g-switch-focus-outline-offset
Focus outline offset
0
Switch keeps the current s/m/l dimensions, slider translation, loader geometry,
transitions, disabled behavior, and focus outline width/color. Checked loader color keeps
following the existing checked track semantic color.
Calendar — 1
Variable
Applied to
Current Gravity UI result when unset
--g-date-calendar-button-border-radius
Shared interactive cell in days, months, quarters, and years modes
4px
The variable is implemented in @gravity-ui/date-components. It applies consistently to
the existing cell surface in default, hover, focus, selected, disabled, and unavailable
states. Popup continues to own calendar popup radius.
Modal — 1
Variable
Applied to
Current Gravity UI result when unset
--g-modal-box-shadow
Modal content shadow
none
Modal already exposes border radius and sizing variables. Adding a shadow must not change
clipping, transition, mobile layout, or content overflow.
Dialog — 6
Variable
Applied to
Current Gravity UI result when unset
--g-dialog-header-font-family
Header caption font family
Current Text variant family per desktop/mobile mode
--g-dialog-header-font-weight
Header caption font weight
Current Text variant weight per desktop/mobile mode
--g-dialog-header-font-size
Header caption font size
Current Text variant size per desktop/mobile mode
--g-dialog-header-line-height
Header caption line height
Current Text variant line height per desktop/mobile mode
--g-dialog-close-button-color
Owned close Button foreground
Current flat Button color
--g-dialog-close-button-color-hover
Owned close Button hover and keyboard-focus color
Current flat Button hover/focus color
Dialog maps this API to the Text caption and close Button that it owns. Consumer CSS does
not select their classes or change Button variables globally. When Dialog variables are
absent, desktop and mobile continue to use their existing distinct Text variants and Button
defaults.
Progress — 1
Variable
Applied to
Current Gravity UI result when unset
--g-progress-border-radius
Progress container and its clipped fill surface
3px
Existing Progress colors, sizes, text, loading animation, and reduced-motion behavior are
unchanged.
Existing API used by compound components
Compound components must use the API of the component that owns a visual surface. This is
part of keeping consumer theme CSS selector-free, but it does not require duplicate
variables.
DropdownMenu uses Popup background, border color, border radius, border width, and shadow
variables, plus
Menu geometry and state variables.
Select uses Popup surface variables and List item radius.
Breadcrumbs uses Popup radius and shadow for its menu popup.
Calendar popup surface remains owned by Popup.
Dialog maps its narrowly scoped variables to the Text and Button it owns without changing
those components globally.
Foreground inheritance, separator exclusion, disabled precedence, and list sizing are
implementation responsibilities. They are not additional public CSS properties.
Theme scopes, portals, and nested popups
Theme authors define variables on a public theme scope, not on Gravity UI selectors:
Application-owned subscopes may unset or replace variables for narrower theme contexts.
They still must not select .g-* classes.
CSS custom properties follow normal DOM inheritance and do not automatically cross an
arbitrary portal boundary. This RFC therefore guarantees portal theming when the portal
container is inside the same public theme scope. Existing public container, popup props, className, or style APIs may be used for a narrower scope.
Root DropdownMenu popups and nested submenu popups mounted in the same theme scope must
consume the same Popup, Menu, and DropdownMenu variables. A theme must not select the
portal container or submenu implementation classes.
Internal implementation requirements
These requirements make the public API sufficient. They do not authorize default visual
changes.
Read every variable at the semantic styling point that owns the property.
Preserve current private fallbacks for every size, mode, and state branch.
Apply Menu item geometry and gap to regular items without applying them to DropdownMenu
separators.
Let separator margin own the complete space around a separator and keep the separator
non-interactive.
Apply configured foreground colors to component-owned icons without changing default
icon colors.
Keep Select padding within its current available width and keep mobile/virtualized
behavior unchanged.
Map SegmentedRadioGroup variables to the existing option surface without changing layout
or focus behavior.
Keep disabled state stronger than checked, selected, active, danger, hover, focus, and
pressed states.
Keep Switch size geometry, translation, loader, and disabled opacity unchanged.
Keep all current accessibility semantics and reduced-motion handling.
Themer support
Themer integration in this RFC covers letter spacing for Caption, Body, Code, Subheader,
Header, and Display in @gravity-ui/uikit-themer, including CSS generation and round-trip
parsing.
Rollout
Implementation may land incrementally by component and package. A variable is delivered
only when:
the component consumes it at the correct semantic part or state;
current default computed styles and visual tests remain unchanged;
size, mode, state, direction, and portal behavior are covered where relevant;
the variable is documented as public CSS API;
an opt-in fixture demonstrates the customized value without private selectors.
If a component cannot satisfy both the CSS API fixture and unchanged default fixtures, its
part of the rollout is deferred. The default Gravity UI style is not changed to make the
migration pass.
Definition of done
All 72 proposed variables are accepted or explicitly adjusted in RFC review.
Legacy List consumes existing --g-list-item-border-radius.
Every accepted variable is documented as public CSS API in its owning package.
Unthemed screenshots remain unchanged for every affected component.
Computed-style fixtures compare before/after defaults for all affected properties.
Size-specific, desktop/mobile, single/multiple, checked/unchecked, and
enabled/disabled default branches remain unchanged where applicable.
Menu fixtures cover geometry, item gap, four logical divider spacings, pressed,
disabled text, danger hover, and state precedence.
DropdownMenu fixtures use grouped public items and verify configurable
margin/color, no stacked item gap, and no Menu item surface on the separator.
SegmentedRadioGroup fixtures cover focus, checked, disabled, disabled-and-checked,
all sizes, configured motion, and reduced motion.
Select fixtures cover all non-mobile sizes, regular and virtualized lists, single and
multiple modes, grouped options, disabled custom content/icons, pressed state, LTR,
and RTL; mobile rendering and overflow remain unchanged.
Switch fixtures cover unchecked, checked, hover, pressed, disabled, loading, focus,
and all sizes without changing existing geometry.
Calendar fixtures cover days, months, quarters, and years modes in default, hover,
focus, selected, disabled, and unavailable states.
Dialog fixtures verify header typography and close-button colors without selecting
nested Text or Button classes.
Root and nested popup fixtures work from a public theme scope without private portal
selectors.
Themer represents all six typography letter-spacing groups.
An integration fixture configures the proposed API using only custom properties on a
public theme scope and contains no Gravity UI selectors.
CSS API extension
Objective
Gravity UI already exposes global semantic tokens and CSS custom properties for many
visual aspects of its components. This RFC proposes an additive extension of that public
CSS API for component parts and states that cannot currently be customized at the correct
scope.
A public theming API should let theme authors customize supported semantic parts by
declaring custom properties on public theme scopes. Consumer styles must not depend on
private
.g-*elements, modifiers, pseudo-elements, DOM structure, or CSS implementationdetails.
This proposal is deliberately non-breaking:
before the change;
their default result.
Scope
The proposal covers public typography and components exported by
@gravity-ui/uikitand@gravity-ui/date-components: Text, Hotkey, Popup, legacy List, Menu, DropdownMenu,SegmentedRadioGroup, Select, Switch, Calendar, Modal, Dialog, and Progress.
Breadcrumbs is included because it composes Popup, but it does not need a new variable. Its
menu popup must consume the existing Popup API.
Goals
selectors.
by Gravity UI.
Non-goals
API selection rules
A new variable belongs in this RFC only when all of the following are true:
required scope.
technique.
variable is absent.
There are two narrow additions for API completeness: an opposite logical side may be
included when it is already independently meaningful in the component, and all members of
an accepted typography family may be included together. These additions cover the mirror
variables and the six Text letter-spacing groups below; they do not justify a general
Cartesian product of properties and states.
The following do not become new CSS API:
Logical and mirror variables
Opposite logical sides are both exposed when they are independently meaningful in the
existing component contract. This is why Menu group-divider padding and margin have both
block-start and block-end variables, and Select group-label padding has both inline-start
and inline-end variables.
Mirrors are not added mechanically. A shorthand already controls both sides for Menu item
padding and DropdownMenu separator margin. Select list margin is intentionally block-axis
only because this proposal does not introduce independent inline-axis spacing.
Compatibility contract
The CSS API is additive, but adding a declaration can still cause a visual regression if
its fallback does not reproduce the existing cascade. Implementations must therefore meet
all of these requirements.
Preserve the current default result
For a direct value, use the current value as the fallback:
For size-, mode-, or state-dependent values, keep a private fallback per existing branch:
A refactor is acceptable only if the computed default is equivalent for every supported
size, mode, state combination, LTR/RTL direction, and relevant input method.
Preserve the current cascade and state precedence
Gravity UI continues to decide which state wins. A public variable supplies a value; it
does not make consumer styles reproduce selectors.
danger.
precedence unless a variable explicitly describes that exact state.
a color variable is set, without changing its default color when variables are absent.
prefers-reduced-motioncontinues to override configurable motion.Preserve public structure and behavior
The implementation must not require:
Internal selectors may of course be used inside Gravity UI to attach the public variables
to the right semantic parts. The prohibition on
.g-*selectors applies to consumertheme CSS, not to the library's own styles.
If an internal cleanup cannot preserve the default result, it must not be bundled into
this RFC. The implementation should either keep the existing internal rule or propose an
additional semantic CSS variable. It must not silently change Gravity UI defaults to make
customization simpler.
Solution proposal
Add 72 new variables and make legacy List consume one existing variable.
--g-list-item-border-radiusProposed CSS variables
The “current Gravity UI result” column is normative for compatibility, not a request to
replace the existing declarations with hard-coded values. The implementation may preserve
the existing cascade or use private fallbacks.
Text — 6
--g-text-caption-letter-spacingcaption-*variantsnormal--g-text-body-letter-spacingbody-*andbody-shortvariantsnormal--g-text-code-letter-spacingcode-*andcode-inline-*variantsnormal--g-text-subheader-letter-spacingsubheader-*variantsnormal--g-text-header-letter-spacingheader-*variantsnormal--g-text-display-letter-spacingdisplay-*variantsnormalAll six groups are included so typography stays coherent and Themer can represent the
same property for every supported group.
Hotkey — 5
--g-hotkey-font-familykbdelements--g-text-body-font-family--g-hotkey-font-weightkbdelements--g-text-body-font-weight--g-hotkey-font-sizekbdelements--g-text-body-1-font-size--g-hotkey-line-heightkbdelements--g-text-body-1-line-height--g-hotkey-letter-spacingkbdelementsnormalThe five variables form a complete Hotkey typography contract while preserving body
typography as the default.
Popup — 1
--g-popup-box-shadowPopup already exposes
--g-popup-background-color,--g-popup-border-color,--g-popup-border-radius, and--g-popup-border-width. The shadow fallback must continueto react to the resolved current border width and color exactly as it does today.
Legacy List — 2 new variables and one existing-variable integration
--g-list-padding0--g-list-item-gap0Legacy List must also consume the existing
--g-list-item-border-radiusvariable on itsitem surface. This is an integration requirement, not a new variable.
--g-list-item-gapapplies only to the non-virtualized flow. Its implementation must notchange virtualization measurements or introduce a new default layout model. Select may
map its own list values to the List it owns; consumer CSS must not select nested List
classes.
Menu — 12
Container, flow, and group dividers
--g-menu-padding--g-spacing-1 0--g-menu-item-gap0--g-menu-group-divider-padding-block-start--g-menu-group-divider-padding-block-end0for s/m/l; current xl end padding--g-menu-group-divider-margin-block-start--g-menu-group-divider-margin-block-end0for s/m/l; current xl end marginGravity UI maps these variables to its current group markup. A theme does not need to know
which list item owns the border. Block-end variables are intentional mirrors: xl Menu
already has independently meaningful end spacing.
Item geometry and states
--g-menu-item-min-height--g-menu-item-padding--g-menu-item-border-radius--g-menu-item-background-color-pressed--g-menu-item-text-color-disabled--g-color-text-secondary--g-menu-item-background-color-danger-hoverThe pressed variable is for an ordinary interactive item. It must not override the current
selected, active, danger, or disabled precedence. Menu-owned foreground icons may use
currentColoror an equivalent internal mapping when a public text-color variable is set;icon wrappers remain private.
Menu container text/background, divider color, normal item text, ordinary hover, selected,
active, and danger text continue to use global semantic tokens.
DropdownMenu — 2
--g-dropdown-menu-separator-margin0.5em 0--g-dropdown-menu-separator-color--g-color-line-generic-solidDropdownMenu already creates separators when the public
itemsAPI contains multipleitem groups. The separator is a semantic DropdownMenu part.
separator-marginconfigures the space around the line;separator-colorconfigures theline color.
Regular adjacent DropdownMenu items use
--g-menu-item-gap. Separator margin owns allspacing around a separator, so Menu item gap must not be added on top of it.
Menu item geometry and state variables must apply to regular DropdownMenu items without
turning a separator into an item surface. The implementation should scope or map the new
Menu API to semantic regular items rather than changing the default separator layout.
DropdownMenu continues to reuse the Popup API for its root popup and submenus.
SegmentedRadioGroup — 21
Container and option
--g-segmented-radio-group-option-inset0--g-segmented-radio-group-font-weight--g-text-body-font-weight--g-segmented-radio-group-background-color--g-segmented-radio-group-border-radius--g-segmented-radio-group-option-border-radius--g-segmented-radio-group-option-divider-color--g-color-line-generic--g-segmented-radio-group-option-border-color--g-color-line-generic--g-segmented-radio-group-option-text-color--g-color-text-complementary--g-segmented-radio-group-option-text-margin-inline10px/13px/18px/25pxThe option inset changes only the option surface. It must not increase the component's
external dimensions or change the public size geometry.
Focus, checked, and disabled
--g-segmented-radio-group-focus-outline-color--g-color-line-misc--g-segmented-radio-group-focus-outline-offset-1px--g-segmented-radio-group-checked-background-color--g-color-base-selection--g-segmented-radio-group-checked-border-color--g-color-line-brand--g-segmented-radio-group-checked-box-shadownone--g-segmented-radio-group-checked-text-color--g-color-text-brand-heavy--g-segmented-radio-group-disabled-background-color--g-segmented-radio-group-disabled-border-color--g-segmented-radio-group-disabled-box-shadownone--g-segmented-radio-group-disabled-text-colorDisabled values take precedence over checked values. The component remains responsible
for the current focus mechanism and for mapping the semantic surface variables to its
existing elements or pseudo-elements.
Motion
--g-segmented-radio-group-transition-duration150ms--g-segmented-radio-group-transition-timing-functionlinearUnder
prefers-reduced-motion: reduce, Gravity UI continues to disable or minimize motionregardless of the configured values.
Select — 8
--g-select-list-margin-block4pxon both block sides--g-select-list-padding0--g-select-list-item-background-color-selected--g-select-list-item-background-color-pressed--g-select-list-option-padding-inline8pxfor s/m,12pxfor l/xl--g-select-list-option-text-color-disabled--g-select-list-group-label-padding-inline-start--g-select-list-group-label-padding-inline-endThe variables apply to non-mobile Select. Mobile Select keeps its current layout and
styles. Selected fallback remains different for single and multiple modes. Pressed applies
only to an enabled option and takes precedence over selected while the option is actually
pressed; when the variable is absent, the current interaction result remains unchanged.
When disabled text color is defined, it reaches the default label, custom option content
through inheritance, and owned foreground icons, including the multiple-selection tick.
When it is not defined, their existing distinct colors remain intact.
The list keeps its normal available width after padding. Supporting the new padding must
not require consumer CSS to select a private list element or set its width.
Virtualized and non-virtualized lists retain their current overflow and measurement model.
Switch — 6
--g-switch-track-background-color-pressed--g-switch-track-background-color-checked-hover--g-switch-slider-background-color--g-color-base-background--g-switch-slider-box-shadownone--g-switch-track-box-shadow-checkednone--g-switch-focus-outline-offset0Switch keeps the current s/m/l dimensions, slider translation, loader geometry,
transitions, disabled behavior, and focus outline width/color. Checked loader color keeps
following the existing checked track semantic color.
Calendar — 1
--g-date-calendar-button-border-radius4pxThe variable is implemented in
@gravity-ui/date-components. It applies consistently tothe existing cell surface in default, hover, focus, selected, disabled, and unavailable
states. Popup continues to own calendar popup radius.
Modal — 1
--g-modal-box-shadownoneModal already exposes border radius and sizing variables. Adding a shadow must not change
clipping, transition, mobile layout, or content overflow.
Dialog — 6
--g-dialog-header-font-family--g-dialog-header-font-weight--g-dialog-header-font-size--g-dialog-header-line-height--g-dialog-close-button-color--g-dialog-close-button-color-hoverDialog maps this API to the Text caption and close Button that it owns. Consumer CSS does
not select their classes or change Button variables globally. When Dialog variables are
absent, desktop and mobile continue to use their existing distinct Text variants and Button
defaults.
Progress — 1
--g-progress-border-radius3pxExisting Progress colors, sizes, text, loading animation, and reduced-motion behavior are
unchanged.
Existing API used by compound components
Compound components must use the API of the component that owns a visual surface. This is
part of keeping consumer theme CSS selector-free, but it does not require duplicate
variables.
variables, plus
Menu geometry and state variables.
those components globally.
Foreground inheritance, separator exclusion, disabled precedence, and list sizing are
implementation responsibilities. They are not additional public CSS properties.
Theme scopes, portals, and nested popups
Theme authors define variables on a public theme scope, not on Gravity UI selectors:
Application-owned subscopes may unset or replace variables for narrower theme contexts.
They still must not select
.g-*classes.CSS custom properties follow normal DOM inheritance and do not automatically cross an
arbitrary portal boundary. This RFC therefore guarantees portal theming when the portal
container is inside the same public theme scope. Existing public
container, popup props,className, orstyleAPIs may be used for a narrower scope.Root DropdownMenu popups and nested submenu popups mounted in the same theme scope must
consume the same Popup, Menu, and DropdownMenu variables. A theme must not select the
portal container or submenu implementation classes.
Internal implementation requirements
These requirements make the public API sufficient. They do not authorize default visual
changes.
separators.
non-interactive.
icon colors.
behavior unchanged.
or focus behavior.
pressed states.
Themer support
Themer integration in this RFC covers letter spacing for Caption, Body, Code, Subheader,
Header, and Display in
@gravity-ui/uikit-themer, including CSS generation and round-tripparsing.
Rollout
Implementation may land incrementally by component and package. A variable is delivered
only when:
If a component cannot satisfy both the CSS API fixture and unchanged default fixtures, its
part of the rollout is deferred. The default Gravity UI style is not changed to make the
migration pass.
Definition of done
--g-list-item-border-radius.enabled/disabled default branches remain unchanged where applicable.
disabled text, danger hover, and state precedence.
itemsand verify configurablemargin/color, no stacked item gap, and no Menu item surface on the separator.
all sizes, configured motion, and reduced motion.
multiple modes, grouped options, disabled custom content/icons, pressed state, LTR,
and RTL; mobile rendering and overflow remain unchanged.
and all sizes without changing existing geometry.
focus, selected, disabled, and unavailable states.
nested Text or Button classes.
selectors.
public theme scope and contains no Gravity UI selectors.