CoUI Chip#
ํ๋ง๋๋ก (๋น๊ฐ๋ฐ์์ฉ)#
์นฉ(Chip)์ ์ด๋ฉ์ผ ๋ฐ๋ ์ฌ๋ ์นธ์ ๋จ๋ ์๊ณ ๋๊ทธ๋ ์ด๋ฆํ๋, ์ผํ๋ชฐ์์ "์์", "์ฌ์ด์ฆ"์ฒ๋ผ ๋๋ฌ์ ๊ณ ๋ฅด๋ ์์ ๋ฒํผํ๋ฅผ ๋ ์ฌ๋ฆฌ๋ฉด ๋ฉ๋๋ค. ์ด ์คํฌ์ ๊ทธ๋ฐ ์์ ๋ผ๋ฒจ/ํ๊ทธ/ํํฐ ๋จ์ถ๋ฅผ ํ๋ฉด์ ๋ง๋ค ๋ ์ผ๊ด๋ ๋ชจ์๊ณผ ๋์์ผ๋ก ์๋์ผ๋ก ๋ง๋ค์ด ์ฃผ๋ ์๋ด์์ ๋๋ค. ๊ธ์๋ง ๋ณด์ฌ์ฃผ๋ ์ ์ ์ธ ํ๊ทธ๋ถํฐ, ๋๋ฌ์ ์ ํํ๋ ํํฐ, X ๋ฒํผ์ผ๋ก ์ง์ธ ์ ์๋ ํ๊ทธ๊น์ง ๊ฐ์ ๋ถํ ํ๋๋ก ์ฒ๋ฆฌํฉ๋๋ค.
๋ฌด์์ยท์ธ์ (๋น๊ฐ๋ฐ์์ฉ)#
- ๋ฌด์์ ํด์ฃผ๋์: ํ๋ฉด์ ๋ค์ด๊ฐ ์์ ๋ผ๋ฒจ/ํ๊ทธ/ํํฐ/์ ํ ๋จ์ถ๋ฅผ ๋์์ธ ๊ท์น์ ๋ง๊ฒ ๋ง๋ค์ด ์ค๋๋ค. ์์, ํฌ๊ธฐ, ์ ํ ์ฌ๋ถ, ์ญ์ (X) ๋ฒํผ ๊ฐ์ ์ต์ ์ ์์ฝ๊ฒ ๋ถ์ผ ์ ์์ต๋๋ค.
-
์ธ์ ์๋ํ๋์:
- ํ๊ทธ ๋ชฉ๋ก์ด๋ ๋ถ๋ฅ ๋ผ๋ฒจ์ ๋ณด์ฌ์ค ๋ (์: "Flutter", "๋์์ธ" ๊ฐ์ ํ์)
- ๋๋ฌ์ ์ผ๊ณ ๋๋ ํํฐ๋ฅผ ๋ง๋ค ๋ (์: ๊ฒ์ ํํฐ ๋ง๋)
- ๋ฐ๋ ์ฌ๋, ์ฐ๋ฝ์ฒ, ์ด๋ฉ์ผ์ฒ๋ผ X ๋ฒํผ์ผ๋ก ์ง์ธ ์ ์๋ ํญ๋ชฉ์ ๋ณด์ฌ์ค ๋
- ๊ฒ์ ์กฐ๊ฑด์ด๋ ์ ๋ ฅํ ๋จ์ด๋ฅผ ์ง์ธ ์ ์๋ ์นฉ์ผ๋ก ํ์ํ ๋
- ์ด๋์ ์ฐ๋์: ๋ชจ๋ฐ์ผ/๋ฐ์คํฌํฑ ์ฑ(Flutter)๊ณผ ์น(Web) ์์ชฝ์์ ๊ฑฐ์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค.
ํต์ฌ ์ฉ์ด (๋น๊ฐ๋ฐ์์ฉ)#
| ์ฉ์ด | ์ฌ์ด ์ค๋ช |
|---|---|
| Chip(์นฉ) | ํ๋ฉด์ ๋จ๋ ์์ ๋ผ๋ฒจ/ํ๊ทธ/๋จ์ถ. ์ด๋ฆํ๋ ์์ ๋ฒํผํ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค. |
| chipColor (์์ ๋ณํ) | ์นฉ์ ์ ํ ๋ง(๊ธฐ๋ณธ ํ์/์ฃผ์/๋ณด์กฐ/์ 3/์ง์ ์ง์ )๋ฅผ ๊ณ ๋ฅด๋ ์ค์ . |
| size (ํฌ๊ธฐ) | ์นฉ์ ํฌ๊ธฐ(์์ฃผ ์์ xs ~ ์์ฃผ ํผ xl)๋ฅผ ๊ณ ๋ฅด๋ ์ค์ . |
| selected (์ ํ๋จ) | ์นฉ์ด ๋๋ ค์ ์ผ์ง ์ํ์ธ์ง ํ์ํ๋ ๊ฐ. |
| onTap (ํญ/๋๋ฅด๊ธฐ) | ์นฉ์ ๋๋ ์ ๋ ์ผ์ด๋ ๋์. |
| onRemove (์ญ์ ) | X ๋ฒํผ์ ๋๋ฌ ์นฉ์ ์ง์ธ ๋ ์ผ์ด๋ ๋์. ์ด ๊ฐ์ด ์์ผ๋ฉด X ๋ฒํผ์ด ๋ณด์ ๋๋ค. |
| leading / trailing (์/๋ค ์ฌ๋กฏ) | ๊ธ์ ์์ด๋ ๋ค์ ์์ด์ฝ ๊ฐ์ ๊ฑธ ๋ฃ๋ ์๋ฆฌ. |
| seedColor (์จ์ ์) | ์ง์ ์ง์ (custom) ์์ ์นฉ์ ๋ฐํ์ด ๋๋ ์. |
| chipStyle (์คํ์ผ) | ๋ชจ์๋ฆฌ ๋ฅ๊ธ๊ธฐ, ์ฌ๋ฐฑ ๋ฑ ์ธ๋ถ ์ธํ์ ๋ฐ๋ก ๋ฐ๊พธ๋ ์ค์ . |
| Legacy / Deprecated | ์๋ ๋ฐฉ์์ด๋ผ ์ ์ฝ๋์์๋ ์ฐ์ง ๋ง์์ผ ํ๋, ๊ณง ์ฌ๋ผ์ง ํญ๋ชฉ. |
Overview#
Chip is the unified cross-platform chip โ a compact element for tags, labels, filters, and selectable items. The same widget covers static tags, selectable filter chips, and removable/dismissible chips. Visual variation is driven by the
chipColor (CoreChipColor) and size (CoreComponentSize) enums; all chrome / dimensional / slot overrides flow through the single
chipStyle slot (CoreChipStyle).
-
Color:
CoreChipColorโcustom,neutral,primary,secondary,tertiary(defaultCoreChipColor.defaultColor=neutral). -
Size:
CoreComponentSizeโxs,sm,md,lg,xl(widget defaultmdon both platforms). -
Style slot:
chipStyle: CoreChipStyle(...)for per-instance chrome micro-tweaks. Semantic enums (chipColor/size/selected) are not part of the style โ pass them as widget params; the theme cannot override them either. -
Both platforms implement the shared
CoreChipContract(<Widget>on Flutter,<Component>on Web), so parameter names and defaults are identical.
For multi-chip selection groups (single/multi select with min/max constraints), a separate ChipGroup<T>
component exists โ this skill covers the single Chip.
Flutter (coui_flutter)#
Import#
import 'package:coui_flutter/coui_flutter.dart';
The barrel is the only supported import โ never import src/ paths. Note: if a file also imports
package:flutter/material.dart directly, hide Material's own chip widget to avoid the name clash:
import 'package:flutter/material.dart' hide Chip;
Basic Usage#
Chip(label: 'Flutter')
Parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
label |
String? |
null |
Text label displayed on the chip |
child |
Widget? |
null |
Custom child widget (alternative to label; wins when both set) |
chipColor |
CoreChipColor |
CoreChipColor.defaultColor (= neutral) |
Color variant |
size |
CoreComponentSize |
CoreComponentSize.md |
Chip size |
selected |
bool |
false |
Whether the chip is selected |
enabled |
bool |
true |
Whether the chip is interactive |
onTap |
VoidCallback? |
null |
Tap callback (only fires when enabled) |
onRemove |
VoidCallback? |
null |
Remove callback โ shows the close (X) button when non-null and enabled |
leading |
Widget? |
null |
Widget displayed before the label |
trailing |
Widget? |
null |
Widget displayed after the label |
seedColor |
CoreColor? |
null |
Seed color (used when chipColor is custom) |
maxLength |
int? |
null |
Max character length for the label (truncates with ...) |
chipStyle |
CoreChipStyle? |
null |
Per-instance chrome / dimensional / slot style |
Color Variants โ CoreChipColor#
Chip(label: 'Default') // neutral (default)
Chip(label: 'Primary', chipColor: CoreChipColor.primary)
Chip(label: 'Secondary', chipColor: CoreChipColor.secondary)
Chip(label: 'Tertiary', chipColor: CoreChipColor.tertiary)
Chip(
label: 'Custom',
chipColor: CoreChipColor.custom,
seedColor: CoreColor.token(CoreColors.primary),
)
CoreChipColor values (full set): custom, neutral, primary,
secondary, tertiary. The widget default is CoreChipColor.defaultColor
= neutral. custom derives its colours from the supplied seedColor
(raw values like CoreColor(0xFF6750A4) also work).
Enum-shorthand (inferred type) is idiomatic in real code:
Chip(label: '๋์์ธ', chipColor: .primary)
Sizes โ CoreComponentSize#
Chip(label: 'XS', size: CoreComponentSize.xs)
Chip(label: 'SM', size: CoreComponentSize.sm)
Chip(label: 'MD', size: CoreComponentSize.md) // default
Chip(label: 'LG', size: CoreComponentSize.lg)
Chip(label: 'XL', size: CoreComponentSize.xl)
CoreComponentSize values: xs, sm, md, lg,
xl.
Selectable Chip#
Chip(
label: '๋์์ธ',
chipColor: CoreChipColor.primary,
selected: isSelected,
onTap: () => toggleSelection(),
)
Selection state is driven by selected; toggling happens in onTap. The resolver picks distinct selected / unselected / disabled colours per
chipColor; the selected border mirrors the selected background. State colour changes animate via
AnimatedContainer with transitionDuration (default 100ms).
Removable Chip#
onRemove renders an internal close (X) button โ but only while enabled is true. The X is built from the unified
Button(variant: .ghost, size: .sm) wrapping Icon(LucideIcons.x), so hover / focus / disabled states are handled by the design system.
Chip(
label: 'user@example.com',
onRemove: () => removeItem(),
)
Leading / Trailing Slots#
Chip(
label: 'Verified',
leading: Icon(LucideIcons.check),
chipColor: CoreChipColor.primary,
)
leading / trailing are arbitrary widgets. Their icon size and colour are driven by the resolved per-size / per-state chip values (an
IconTheme is applied around each slot), so you do not set them manually. The slotโlabel spacers are
Gap widgets driven by the size-aware iconLabelGapStyle slot.
Label Truncation#
Chip(label: 'A very long tag name', maxLength: 10) // renders 'A very lon...'
The label is also rendered with maxLines: 1 + ellipsis overflow.
Disabled Chip#
Chip(label: 'Unavailable', enabled: false)
Disabled chips ignore onTap, hide the close button, show the forbidden cursor, and swap to the disabled colour set.
Per-instance Style โ chipStyle: CoreChipStyle(...)#
Every chrome / dimensional / slot tweak flows through the single chipStyle slot. Semantic enums (chipColor
/ size / selected) stay as widget parameters โ do not pack them into the style.
Chip(
label: 'Custom radius',
chipStyle: const CoreChipStyle(
borderRadius: CoreBorderRadius.all(CoreRadius.radius8),
borderWidth: CoreStrokeWidth.stroke1,
padding: CoreEdgeInsets.symmetric(
horizontal: CoreSpace.space12,
vertical: CoreSpace.space4,
),
labelStyle: CoreTextStyle(fontSize: CoreFontSize.s14),
leadingIconStyle: CoreIconStyle(size: CoreSize.size16),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space4),
),
)
CoreChipStyle fields:
| Field | Type | Description |
|---|---|---|
backgroundColor |
CoreColor? |
Panel background fill override (wins across every state) |
foregroundColor |
CoreColor? |
Foreground (label) colour override |
borderColor | CoreColor? | Border stroke colour |
borderWidth |
double? |
Border stroke width (logical px; default CoreStrokeWidth.stroke1) |
borderRadius |
CoreBorderRadius? |
Corner radius (default: pill radius9999) |
padding | CoreEdgeInsets? | Padding |
iconLabelGapStyle |
CoreGapStyle? |
Gap between leading / label / trailing / close slots |
closeIconStyle |
CoreIconStyle? |
Close (X) icon style |
transitionDuration |
Duration? |
Hover / focus / state colour transition (default 100ms) |
labelStyle |
CoreTextStyle? |
Label text style override (role default: bodySmall token) |
leadingIconStyle |
CoreIconStyle? |
Leading icon style override |
trailingIconStyle |
CoreIconStyle? |
Trailing icon style override (does not affect the close button) |
closeButtonStyle |
CoreButtonStyle? |
Close-button chrome (padding / iconStyle) forwarded to the internal ghost Button |
It also exposes merge, copyWith, and the shared design-system defaults as statics:
defaultBorderRadius (pill, radius9999), defaultBorderWidth (stroke1),
defaultLabelStyle (bodySmall token), defaultTransitionDuration
(100ms), defaultDisabledOpacity, defaultHoverOpacity, defaultCustomDisabledBackgroundColor
/ defaultCustomDisabledForegroundColor / defaultCustomDisabledBorderColor,
defaultCustomSelectedForegroundColor (white), defaultsBySize, and defaultsByVariant
(a Map<CoreChipColor, CoreChipColorStyle>).
Chip Group Pattern#
Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final tag in tags)
Chip(
label: tag,
onRemove: () => removeTag(tag),
),
],
)
For managed single/multi selection over a list of items, prefer the dedicated ChipGroup<T>
component (items + chipBuilder + onChanged).
Web (coui_web / Jaspr)#
Import#
import 'package:coui_web/coui_web.dart';
The barrel re-exports Jaspr (Component, Styles, dom helpers like div
/ span / text) and coui_core, so no separate Jaspr import is needed.
Basic Usage#
The Web Chip has the same params and defaults; child / leading
/ trailing are Jaspr Component, callbacks are CoreVoidCallback?. It renders as a
<div> pill with Tailwind classes plus inline styles synthesized by the resolver.
Chip(label: 'Flutter')
Removable Chip#
Chip(
label: 'user@example.com',
onRemove: () => removeItem(),
)
Same rule as Flutter: the close button renders only when onRemove != null && enabled, via the unified
Button(variant: .ghost, size: .sm) + Icon(LucideIcons.x).
Selectable Chip#
Chip(
label: '๋์์ธ',
chipColor: CoreChipColor.primary,
selected: isSelected,
onTap: () => toggleSelection(),
)
onTap registers a DOM click event (only when enabled). Cursor classes follow state:
cursor-not-allowed when disabled, cursor-pointer when interactive. State colours fade with an inline
transition rule mirroring Flutter's AnimatedContainer.
Leading Slot (Component)#
Chip(
label: 'Verified',
leading: const Icon(LucideIcons.check),
chipColor: CoreChipColor.primary,
)
Leading / trailing slots are wrapped in inline-flex <span>s; the slotโlabel spacers render as
Gap components (same iconLabelGapStyle slot as Flutter).
Custom Child#
When using child instead of label, supply a Component (child
wins when both are set):
Chip(
child: span([text('Custom')]),
onRemove: () => removeItem(),
)
Disabled / Accessibility#
Chip(label: 'Unavailable', enabled: false)
A disabled Web chip gets aria-disabled="true", drops the click handler, and hides the close button.
Web-only extras#
The Web constructor additionally accepts id. The component also provides a copyWith(...)
method covering every chip parameter.
Parameters (both platforms)#
| Parameter | Type (Flutter) | Type (Web) | Default | Notes |
|---|---|---|---|---|
label |
String? |
String? |
null |
Plain-text label |
child |
Widget? |
Component? |
null |
Custom content; wins over label |
chipColor |
CoreChipColor |
CoreChipColor |
defaultColor (= neutral) |
custom / neutral / primary / secondary / tertiary |
size |
CoreComponentSize |
CoreComponentSize |
md |
xs / sm / md / lg / xl |
selected |
bool |
bool |
false |
Selected state |
enabled |
bool |
bool |
true |
Gates onTap and the close button |
onTap |
VoidCallback? |
CoreVoidCallback? |
null |
Tap / click |
onRemove |
VoidCallback? |
CoreVoidCallback? |
null |
Shows close (X) button when non-null and enabled |
leading |
Widget? |
Component? |
null |
Before the label |
trailing |
Widget? |
Component? |
null |
After the label |
seedColor |
CoreColor? |
CoreColor? |
null |
For chipColor: custom |
maxLength |
int? |
int? |
null |
Label truncation with ... |
chipStyle |
CoreChipStyle? |
CoreChipStyle? |
null |
Per-instance chrome/slot style |
Web-only: id, plus copyWith(...).
Color / Size token tables#
Both platforms resolve from the same single source of truth in coui_core (CoreChipStyle.defaultsByVariant
/ CoreChipStyle.defaultsBySize).
Colors (CoreChipColor โ full set; each entry is a CoreChipColorStyle):
| chipColor | Unselected (bg / fg / border) | Selected (bg / fg / border) | Disabled (bg / fg / border) |
|---|---|---|---|
neutral |
transparent / onSurface / outline |
surfaceContainerHigh / onSurface / surfaceContainerHigh |
surfaceContainer / surfaceContainerHigh / surfaceContainer |
primary |
transparent / primary / primary |
primary / onPrimary / primary |
surfaceContainer / surfaceContainerHigh / surfaceContainer |
secondary |
transparent / secondary / secondary |
secondary / onSecondary / secondary |
surfaceContainer / surfaceContainerHigh / surfaceContainer |
tertiary |
transparent / tertiary / tertiary |
tertiary / onTertiary / tertiary |
surfaceContainer / surfaceContainerHigh / surfaceContainer |
custom |
transparent / seed / seed | seed / white / seed | surfaceContainer / surfaceContainerHigh / surfaceContainer |
The selected border always mirrors the selected background. custom has no table entry โ its colours derive from
seedColor at use-site (white selected label via defaultCustomSelectedForegroundColor).
Sizes (CoreComponentSize โ full set; token values, pre-scaling):
| Size | Padding (H ร V) | Label font size | Leading/Trailing icon | Close icon | Iconโlabel gap |
|---|---|---|---|---|---|
xs |
space6 ร 0 |
s10 |
size12 |
size12 |
space2 |
sm |
space8 ร space2 |
s12 |
size14 |
size14 |
space4 |
md |
space12 ร space4 |
s14 |
size16 |
size16 |
space4 |
lg |
space14 ร space6 |
s16 |
size18 |
size18 |
space6 |
xl |
space16 ร space8 |
s18 |
size20 |
size20 |
space8 |
Label typography role is bodySmall (CoreChipStyle.defaultLabelStyle) across every size โ only the per-size font size varies. Shape default: pill (radius9999); border width default
CoreStrokeWidth.stroke1.
Project-level theming โ CoreChipTheme#
CoreChipTheme has exactly two slots โ style and variantStyles (keyed by
CoreChipColor). Semantic identifiers (chipColor / size) are widget-only; the theme cannot set them. Resolve chain:
design-system default (CoreChipStyle.defaultsBySize[size])
โ CoreChipTheme.style // ํ๋ก์ ํธ ๊ณตํต
โ CoreChipTheme.variantStyles[widget.chipColor]
โ parent component slot override
โ widget.chipStyle // ์ธ์คํด์ค๋ณ
const CoreChipTheme(
style: CoreChipStyle(
borderRadius: CoreBorderRadius.all(CoreRadius.radius8),
),
variantStyles: {
CoreChipColor.primary: CoreChipStyle(
borderWidth: CoreStrokeWidth.stroke1,
),
},
)
Register it on the app theme's coreComponentTheme.chip slot โ both platform resolvers read
theme.coreComponentTheme?.chip.
Flutter โ Web Differences#
These are the only behavioural differences confirmed in source โ everything else (enums, parameter names, defaults, the
chipStyle slot) is identical.
| Aspect | Flutter | Web |
|---|---|---|
| Widget class | Chip (StatelessWidget, CoreChipContract<Widget>) |
Chip (UiComponent, CoreChipContract<Component>) |
child / leading / trailing type |
Widget? |
Component? |
| Callback type | VoidCallback? | CoreVoidCallback? |
| Label render | Text(label) with ellipsis, maxLines: 1 |
<span class="truncate"> inside the pill <div> |
| Tap wiring | GestureDetector(onTap: ...) when enabled |
DOM click event when enabled |
| Disabled affordance | MouseRegion forbidden cursor |
aria-disabled="true" + cursor-not-allowed class |
| State transition | AnimatedContainer(duration: transitionDuration) |
inline CSS transition (background / color / border) |
| Extra ctor params | โ | id; also copyWith(...) |
Both platforms render the close (X) button via the unified Button(variant: .ghost, size: .sm)
wrapping Icon(LucideIcons.x), and both insert Gap(gapStyle: ...) spacers between adjacent slots.
When to Use#
- Tag lists and category labels (static
Chip(label: ...)) - Filter selection bars (
selected+onTap) - User / contact / email selection chips with removal (
onRemove) - Removable search criteria / token input chips
- Managed selection over a whole set โ
ChipGroup<T>instead
v0.127 Migration (Legacy)#
v0.127 is a de-prefix hard break: the widget class was renamed and no Co-prefixed alias remains.
Core* contract names (CoreChipColor, CoreChipStyle, CoreChipColorStyle,
CoreChipTheme, CoreChipContract, CoreComponentSize) are unchanged.
| โ Legacy (pre-0.127, removed) | โ v0.127 |
|---|---|
โ CoChip(label: โฆ) | Chip(label: โฆ) |
โ CoChip(chipColor: CoreChipColor.primary, selected: โฆ) |
Chip(chipColor: CoreChipColor.primary, selected: โฆ) |
โ CoIcon(CoLucideIcons.check) | Icon(LucideIcons.check) |
โ CoLucideIcons.x (close icon internals) | LucideIcons.x |
โ CoButton(variant: ghost) (close-button internals) |
Button(variant: .ghost) |
โ CoGap(โฆ) (slot spacer internals) | Gap(โฆ) |
โ CoChipGroup(โฆ) | ChipGroup(โฆ) |
Additional migration notes:
-
โ The old deprecated legacy surface (
Chip.button(...)factory, standaloneChipStyle/ChipTheme/ChipCloseButtonclasses,CoreChipSelectedStyle/CoreChipUnselectedStyleenums) was removed entirely โ v0.127 chip source carries no deprecated members. UseChip+chipColor/size/chipStyle(CoreChipStyle) /CoreChipThemeonly. -
Import stays the single barrel (
package:coui_flutter/coui_flutter.dart/package:coui_web/coui_web.dart); with the bareChipname, hide Material's chip when importingmaterial.dartdirectly (import 'package:flutter/material.dart' hide Chip;).