sx — style shorthand
sx is lh-ui’s inline style shorthand: use mt: 4 instead of style="margin-top: 32px", and p: 2 instead of writing all four paddings at once. It is not Tailwind (it generates no classes and pulls in no CSS) — it evaluates a props bag into a Record<string, string> at runtime, which the UI layer binds to :style.
When to use it
Section titled “When to use it”- Inside the component library:
sxlets components avoid adding a new prop for “layout tweaks” —<LhButton sx={{ mt: 2 }} />pushes the button away from what is above it. - Spacing that needs to change with data: the number-to-pixel conversion (
×8) gives “move a bit closer to me” semantics a stable scale. - Don’t use it for: systematic visual design. sx is a “temporary tweak at the call site”; design tokens like theme colors and control heights still go through CSS variables (see Theme system). Writing
color: 'var(--lh-primary-color)'in sx works, but having the component consume the token directly is preferred.
Import
Section titled “Import”// Precise import (recommended, tree-shaking friendly)import { sxToStyle, mergeSx } from '@lh-ui/share/core/shared/sx'import type { LhSxProps } from '@lh-ui/share/core/shared/sx'
// Or from the aggregate entry pointimport { sxToStyle } from '@lh-ui/share/core/shared'Function table
Section titled “Function table”| Function | Signature | Returns | Purpose |
|---|---|---|---|
sxToStyle |
(sx: LhSxProps) => Record<string, string> |
Inline style object | Evaluates an sx props bag into CSS styles |
mergeSx |
(...list: (LhSxProps | undefined | null)[]) => LhSxProps |
Merged sx | Later entries overwrite earlier ones, so the UI layer can combine “default sx + caller sx” |
sxToStyle precedence rules
Section titled “sxToStyle precedence rules”“Single side > axis pair > shorthand”, regardless of key order. The implementation writes into the style object in the fixed order shorthand → axis pair → single side, with later writes overwriting earlier ones, so no matter how you order the keys:
sxToStyle({ m: 2, mt: 4 }) // { marginTop: '32px', marginRight: '16px', marginBottom: '16px', marginLeft: '16px' }sxToStyle({ mt: 4, m: 2 }) // identicalsxToStyle({ my: 2, mt: 4 }) // the mt single side wins: 32px; mb stays 16pxThis rule lets the component library safely write “default bag + user override”: a user-supplied mt always overrides the component’s internal m.
Numbers and strings
Section titled “Numbers and strings”- Numbers are converted at
×8px:mt: 4→32px. 8 is lh-ui’s spacing base (the same scale as the--lh-spacing-*tokens). - Strings are passed through as-is:
w: '50%'→50%,p: '1rem'→1rem. - Layout keys are not converted: the values of
display/flex/flexDirection/alignItems/justifyContentare written directly as strings (flexDirection: 'column'does not becomecolumnpx). - All keys are tested with
!== undefined: passing an empty string''counts as an explicit clear and is written.
Type definition
Section titled “Type definition”export interface LhSxProps { // Size w?: string | number h?: string | number // Margin: single side / axis pair / shorthand mt?: string | number mr?: string | number mb?: string | number ml?: string | number my?: string | number mx?: string | number m?: string | number // Padding: same shape pt?: string | number pr?: string | number pb?: string | number pl?: string | number py?: string | number px?: string | number p?: string | number // Layout display?: string flex?: string flexDirection?: 'row' | 'column' alignItems?: string justifyContent?: string // Allow downstream custom keys; unknown keys are silently dropped [key: string]: any}The index signature [key: string]: any is deliberate: each UI library puts sx?: LhSxProps alongside component-specific props in its own LhXxxProps, and the index signature keeps the types from conflicting. Unknown keys are dropped during evaluation and never leak into the styles.
Usage examples
Section titled “Usage examples”Pure function
Section titled “Pure function”sxToStyle({ mt: 2, px: 3, display: 'flex', alignItems: 'center' })// {// marginTop: '16px',// paddingLeft: '24px',// paddingRight: '24px',// display: 'flex',// alignItems: 'center',// }Wiring per port
Section titled “Wiring per port”<!-- Vue3 / Vue2: bind directly to :style --><template> <lh-button :style="sxToStyle($props.sx)">Button</lh-button></template>// React<div style={sxToStyle({ my: 4, w: '100%' })} />// uniapp: sxToStyle outputs a plain object, feed it straight into the style binding// (the ×8px number conversion may need platform-specific handling on non-h5 targets,// handled by the UI layer)The typical use of mergeSx is combining defaults inside a component:
// Inside the componentconst merged = mergeSx({ py: 2, px: 4 }, props.sx) // the user's sx overrides the defaultsconst style = sxToStyle(merged) // evaluated once, the precedence rules settle onceCaveats
Section titled “Caveats”mergeSxis a shallow merge (Object.assign), not recursive; sx values are all scalars, so a shallow merge is enough.sxToStylereturns{}for falsy input:sxToStyle(undefined)is an empty object, so the UI layer can call it unconditionally.- The 8px base of the number conversion is aligned with the token system, but sx itself does not reference CSS variables — it produces computed literals, suited for “offset this one call a bit”, not for “change with the theme”.
- Cross-port: uniapp’s rpx on non-h5 targets must be converted by the UI layer; the sx core only emits px.
@lh-ui/share/core/sx → sxToStyle( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue