๐ก See CLAUDE.md for the full guide.
This document is a quick reference summary for CoUI component usage.
Typography API Guide#
Project Standard: context.textStyles#
This project uses context.textStyles (used in 22 files).
// โ
Project standard: use textStyles
style: context.textStyles.lgSemibold
style: context.textStyles.smSemibold
style: context.textStyles.sm.merge(context.textStyles.medium)
// โ
copyWith composition
style: context.textStyles.lgSemibold.copyWith(
color: context.appColors.neutral5,
)
API Selection#
| API | Status |
context.textStyles |
โ
Project standard (used in 22 files) |
context.typography | โ Prohibited (0 occurrences) |
Combination Getters (Frequently Used)#
| Getter | Composition | Example |
lgSemibold | 16px + 600 weight | Headings, emphasis text |
smSemibold | 12px + 600 weight | Labels, badges |
baseMedium | 14px + 500 weight | Body emphasis |
sm | 12px | Secondary text |
xs | 10px | Caption |
Complete Font Size List#
| Getter | Size | Purpose |
xs | 10px | Caption, annotation |
sm | 12px | Secondary text, labels |
base | 14px | Default body |
lg | 16px | Emphasis body, subheading |
xl | 20px | Heading |
x2l | 24px | Large heading |
x3l | 30px | Page heading |
x4l | 36px | Extra large heading |
x5l | 48px | Hero text |
TextField features Parameter#
โ ๏ธ deprecated: The leading and trailing parameters are deprecated.
// โ
New API: features parameter
TextField(
controller: controller,
placeholder: const Text('์
๋ ฅํด์ฃผ์ธ์'),
filled: true, // ๋ฐฐ๊ฒฝ์ ์ ์ฉ (base200)
features: [
InputFeature.leading(const Icon(Icons.search, size: 20)),
InputFeature.trailing(const Icon(Icons.clear, size: 18)),
],
onChanged: (value) => ...,
)
// โ deprecated: leading/trailing parameters
TextField(
leading: Icon(Icons.search), // deprecated
trailing: Icon(Icons.clear), // deprecated
)
TextField Key Properties#
| Property | Type | Description |
filled |
bool |
true applies background color (base200) |
border |
Border |
Border setting (Border.all(color: context.colorScheme.base300)) |
borderRadius | BorderRadius | Border radius |
features |
List |
leading, trailing, clear, etc. |
โ ๏ธ Note: ButtonSize.medium does not exist!
| Constant | Scale | Purpose |
ButtonSize.xSmall | 0.5x | Very small button |
ButtonSize.small | 0.75x | Small button |
ButtonSize.normal | 1.0x | Default |
ButtonSize.large | 2.0x | Large button |
ButtonSize.xLarge | 3.0x | Very large button |
// โ
CORRECT: Use existing sizes
ButtonStyle.primary(size: .normal)
ButtonStyle.primary(size: .small)
// โ WRONG: medium doesn't exist (compile error)
ButtonStyle.primary(size: .medium) // ์๋ฌ!
| Constant | Purpose |
ButtonDensity.normal | Normal density |
ButtonDensity.comfortable | Comfortable padding |
ButtonDensity.dense |
Compact button (buttons in tables) |
ButtonDensity.compact | Minimum padding |
ButtonDensity.icon | Icon only |
ButtonDensity.iconComfortable | Icon + comfortable padding |
ButtonDensity.iconDense | Icon + compact |
// Compact button in table
Button.outline(
style: const ButtonStyle.outline(density: .dense),
onPressed: handleTap,
child: const Text('๋ณต์ฌ'),
)
Badge Component#
Badge component for status display. Used with ButtonStyle.
Badge Types#
| Component | Purpose | Default Color |
PrimaryBadge | Positive status (on sale, active, etc.) | primary |
DestructiveBadge |
Negative status (stopped, deleted, etc.) |
destructive (red) |
Usage Examples#
// โ
Basic usage
const PrimaryBadge(child: Text('ํ๋งคin progress'))
const DestructiveBadge(child: Text('ํ๋งค in progress์ง'))
// โ
Make compact with density adjustment
const PrimaryBadge(
style: ButtonStyle.primary(density: .dense),
child: Text('ํ๋งคin progress'),
)
const DestructiveBadge(
style: ButtonStyle.destructive(density: .dense),
child: Text('ํ๋งค in progress์ง'),
)
// โ
Conditional badge
child: book.isActive
? const PrimaryBadge(
style: ButtonStyle.primary(density: .dense),
child: Text('ํ๋งคin progress'),
)
: const DestructiveBadge(
style: ButtonStyle.destructive(density: .dense),
child: Text('ํ๋งค in progress์ง'),
),
Gap & Insets Constants#
Gap Usage#
const Gap.s1() // 4px
const Gap.s2() // 8px
const Gap.s4() // 16px
Gap(Insets.small) // 8px
Gap(Insets.medium) // 16px
Gap(Insets.large) // 24px
Insets Constants Table#
| Constant | Value | Purpose |
Insets.xSmall | 4px | Icon spacing |
Insets.small | 8px | Element spacing |
Insets.medium | 16px | Section padding |
Insets.large | 24px | Card padding |
Insets.xLarge | 32px | Page margin |
Loading/Skeleton Patterns#
// Skeleton loading (entire widget)
widget.skeletonizer(enabled: state.isLoading)
// Conditional loading (spinner)
widget.loadingOr(
isLoading: state.isLoading,
loadingWidget: const CircularProgressIndicator(),
)
// Empty state handling
listWidget.emptyOrWhen(
condition: () => list.isEmpty,
emptyWidget: const EmptyStateWidget(),
)
Complete Page Example#
class MyPage extends HookWidget {
const MyPage({super.key});
@override
Widget build(BuildContext context) {
final appColors = context.appColors;
return BlocProvider(
create: (_) => MyBloc(),
child: BlocBuilder<MyBloc, MyState>(
builder: (context, state) {
return Scaffold(
headers: [
AppBar(
title: Text(
'ํ์ด์ง ์ ๋ชฉ',
style: context.textStyles.lgSemibold.copyWith(
color: appColors.neutral5,
),
),
),
],
child: Padding(
padding: const .symmetric(horizontal: Insets.medium),
child: Column(
crossAxisAlignment: .start,
children: [
const Gap(Insets.medium),
TextField(
placeholder: const Text('์ด๋ฆ์ ์
๋ ฅํ์ธ์'),
features: const [
InputFeature.leading(Icon(Icons.person, size: 20)),
],
onChanged: (value) => context.read<MyBloc>().add(
MyEvent.nameChanged(value),
),
),
const Gap(Insets.medium),
Button.primary(
expanded: true,
enabled: state.isValid,
onPressed: () => context.read<MyBloc>().add(
const MyEvent.submitted(),
),
child: const Text('์ ์ฅ'),
),
],
),
).skeletonizer(enabled: state.isLoading),
);
},
),
);
}
}
Color Scheme (Console UI)#
AppColors Neutral Series#
| Color | HEX | Purpose |
neutral100 | #FFFFFF | White background |
neutral95 |
#F9FAFB |
Header/section background (distinct from body) |
neutral85 | #F4F5F6 | Light gray |
neutral80 | #ECEEEF | Default border |
neutral70 |
#B4B8C4 |
Dark border (clear separator) |
neutral60 | #8F929A | Inactive text |
neutral30 | - | Body text |
// Header background (distinct from body)
color: context.appColors.neutral95,
// Dark border (clear separation)
border: Border(
right: .new(color: context.appColors.neutral70),
),
// Default border
border: Border.all(color: context.appColors.neutral80),
ColorScheme Base Series#
CoUI's color scheme.
| Color | Purpose |
base100 | Lightest background |
base200 | Medium background (filled TextField) |
base300 | Darkest background/border |
// TextField background
TextField(
filled: true, // base200 ๋ฐฐ๊ฒฝ
border: Border.all(color: context.colorScheme.base300),
)
// Filter dialog border
OutlinedContainer(
backgroundColor: theme.colorScheme.base100,
borderColor: theme.colorScheme.base300, // ์งํ ํ
๋๋ฆฌ
)
Reference Documents#
Refer to CLAUDE.md for the following:
- Color rules (AppColors, context.appColors)
- Dot Shorthand rules (Dart 3.10+)
- Button/IconButton usage
- BLoC Event/State patterns (sealed class)
- BLoC async handlers (isClosed check)
- Complete list of Context Extensions