Skip to content

Commit 3abc13a

Browse files
authored
Generated button + updated component authoring skill and instructions (#4176)
* add fluent style slot behaviors * update component types and slot implementations * fix bugs around slot types and jsx spreading * remove extra file from this branch * clean up slot types * update instructions and fix typing issues with usePressableState * consolidate on useSlot and useOptionalSlot * consolidate old and new slot patterns into a core set of APIs * sanitize exports and mark internal only routines more clearly * add changeset * add v0 and v1 instruction files * move button spec into the button component directory * initial button seeding * rename button from tsx to ts * colocate the SPEC.md file with the component * first draft with the agent driven build * move Icon to primitives rather than components to distinguish patterns * update structure of flex tokens to optimize usage * combine mappings into new yaml file and update instructions * update lockfile and metro config * fix flex style mapping error * work in progress changes for styling * more modifications for component building * in progress changes * rework of the color/style factory helpers * apply branchedStyle helpers to components * rework styles with new instructions * add change file * fix storybook build, optimize instructions * update storybook guidance and add stories * fix styling bugs and enhance Icon stories * restructure authoring skill to use sub-references
1 parent b38e315 commit 3abc13a

111 files changed

Lines changed: 7727 additions & 2463 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/calm-pumas-group.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@fluentui-react-native/design": minor
3+
---
4+
5+
Group Flex tokens by category and remove category prefixes from individual token names

.changeset/few-zebras-love.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@uifabricshared/foundation-compose": patch
3+
"@fluentui-react-native/framework": patch
4+
"@fluentui-react-native/design": patch
5+
"@fluentui-react-native/framework-base": patch
6+
---
7+
8+
Updated packages with agent instructions and type fixes
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
name: agentic-component-authoring
3+
description: Generate or update Fluent UI React Native components in packages/agentic-components. Use for component APIs, slots, state hooks, token styling, render functions, tests, stories, and spec-driven component work.
4+
license: MIT
5+
---
6+
7+
# Agentic component authoring
8+
9+
Build components in `packages/agentic-components` as React Native adaptations of the Fluent UI v9 component pattern.
10+
This skill is the workflow router. Load only the references needed for the current change instead of placing every
11+
authoring rule in one always-loaded instruction file.
12+
13+
## Choose the component kind
14+
15+
- Higher-order components live in `src/components`. Read the
16+
[higher-order component instructions](../../../packages/agentic-components/src/components/AGENTS.md).
17+
- Primitive components live in `src/primitives`. Read the
18+
[primitive instructions](../../../packages/agentic-components/src/primitives/AGENTS.md).
19+
- Work on the Storybook application, native projects, bundling, or CocoaPods belongs to the
20+
[agentic Storybook development skill](../agentic-storybook-development/SKILL.md), not this component workflow.
21+
22+
## Load focused references
23+
24+
| Work | Reference |
25+
| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
26+
| Public props, slots, state types, native prop exposure, or exports | [Types and slots](references/types-and-slots.md) |
27+
| Defaults, derived state, interaction hooks, accessibility, or slot construction | [State and accessibility](references/state-and-accessibility.md) |
28+
| Tokens, style factories, theme caching, state precedence, or slot style application | [Styles and tokens](references/styles-and-tokens.md) |
29+
| Pure slot rendering, component assembly, or display names | [Rendering and assembly](references/rendering.md) |
30+
| Runtime tests, type tests, snapshots, Storybook stories, or validation | [Tests and stories](references/tests-and-stories.md) |
31+
32+
A new higher-order component normally needs every reference. A focused fix should load only the affected reference and
33+
its immediate neighbors. Keep the component's colocated `SPEC.md` and companion files authoritative for its contract.
34+
35+
## Workflow
36+
37+
1. Read the repository and package instructions, the component `SPEC.md`, and every companion file referenced by the
38+
spec.
39+
2. Inspect the closest canonical implementation. Use
40+
[`components/button`](../../../packages/agentic-components/src/components/button) for a styled higher-order component
41+
and [`primitives/icon`](../../../packages/agentic-components/src/primitives/icon) for a direct primitive.
42+
3. Establish the public contract before implementation: variants, slots, native props, accessibility, interaction
43+
states, and platform behavior.
44+
4. Implement in dependency order: types and slots, state and accessibility, styles and slot props, pure rendering,
45+
component assembly, and explicit exports.
46+
5. Preserve the specification. Record a genuine token or platform gap rather than substituting an unrelated value or
47+
web-only behavior.
48+
6. Add focused tests and stories that exercise the public API and the resolved native output.
49+
7. Run the smallest declared validation command while iterating. Finish with package format, lint, build, and tests; run
50+
the Storybook bundle for story changes and the root build when public types, manifests, or project references change.
51+
52+
Do not divide one component implementation into separate sub-agent or sub-skill phases. Its types, state, styling, and
53+
rendering form one contract and should remain in one implementation context.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Rendering and assembly
2+
3+
Use this reference for `render<Component>.tsx`, `<component>.ts`, slot ordering, conditional structure, display names,
4+
and package exports. The canonical examples are
5+
[`renderButton.tsx`](../../../../packages/agentic-components/src/components/button/renderButton.tsx) and
6+
[`button.ts`](../../../../packages/agentic-components/src/components/button/button.ts).
7+
8+
## Keep render functions pure
9+
10+
The render function receives resolved state and returns the final element tree.
11+
12+
- Add `/** @jsxImportSource @fluentui-react-native/framework-base */` when rendering slot components.
13+
- Do not call hooks.
14+
- Do not read themes or tokens.
15+
- Do not create styles.
16+
- Do not reinterpret defaults or accessibility.
17+
- Do not mutate slot props.
18+
19+
All those decisions belong to earlier stages.
20+
21+
## Render slot functions directly
22+
23+
Capitalize local optional slot variables when that improves readability. Render required roots through the state slot:
24+
25+
```tsx
26+
return <state.root>{content}</state.root>;
27+
```
28+
29+
Keep slot order visible in JSX. Button renders its active icon before or after content according to `iconPosition`.
30+
Avoid array construction or opaque helper loops when direct JSX makes the public ordering contract clearer.
31+
32+
## Resolve conditional structure from state
33+
34+
The render function may select among already-resolved slots and compose conditional layout:
35+
36+
- Button chooses `selectedIcon` when selected and falls back to `icon`.
37+
- Toggle content uses the private container, hidden ghost, and visible label.
38+
- Ordinary content renders without that wrapper.
39+
40+
Do not create missing slots in the render stage. If a structural element can exist, represent it in the state type and
41+
construct it in the state hook.
42+
43+
Components that own children should render only their declared slots. Do not spread native `children` into the tree and
44+
let consumers bypass slot order.
45+
46+
## Assemble the component in one small file
47+
48+
`<component>.ts` should show the pipeline without embedding stage logic:
49+
50+
```ts
51+
export const Component = (props: ComponentProps) => {
52+
const state = useComponent_unstable(props);
53+
useApplyStyles_unstable(state);
54+
return renderComponent_unstable(state);
55+
};
56+
```
57+
58+
Set `displayName` for diagnostics and Storybook metadata. Keep unstable stage functions named consistently so tests and
59+
future composition work can identify them. Do not wrap the component in memoization or another boundary without measured
60+
need and repository precedent.
61+
62+
After assembly, update the package public surface according to the
63+
[types and slots export rules](types-and-slots.md#exports).
64+
65+
## Review checklist
66+
67+
- Render contains JSX and conditional structure only.
68+
- Slot order matches the spec.
69+
- Optional and replacement slots fall back intentionally.
70+
- State-only structure does not leak into public props.
71+
- The assembly file is a readable state -> styles -> render pipeline.
72+
- `displayName` is present and the public surface follows the types and slots export rules.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# State and accessibility
2+
3+
Use this reference for `<Component>StateProps`, `use<Component>.ts`, defaults, interaction hooks, accessibility, derived
4+
state, and slot construction. The canonical example is
5+
[`useButton.ts`](../../../../packages/agentic-components/src/components/button/useButton.ts).
6+
7+
## State hook responsibilities
8+
9+
The state hook owns:
10+
11+
1. Destructuring component-owned props while preserving unhandled native props.
12+
2. Applying behavioral defaults.
13+
3. Deriving facts needed by accessibility, styles, and rendering.
14+
4. Validating development-time usage.
15+
5. Reading theme state.
16+
6. Configuring the native interaction hook.
17+
7. Constructing required and optional slots.
18+
8. Returning one complete resolved state object.
19+
20+
Do not resolve token styles or render elements in this hook.
21+
22+
## Preserve raw presence before applying defaults
23+
24+
Derive presence-sensitive behavior from raw props. Button computes `hasContent`, `hasIcon`, and `hasSelectedIcon` before
25+
constructing slots. It uses those values to derive `iconOnly`, while `selected !== undefined` determines whether toggle
26+
semantics are enabled even when `selected` is false.
27+
28+
Apply context-sensitive defaults only after those facts are known. Button defaults shape to `circle` for icon-only use
29+
and `rounded` otherwise.
30+
31+
## Merge accessibility deliberately
32+
33+
Start with consumer-provided accessibility state, then apply component-owned semantics:
34+
35+
```ts
36+
accessibilityState: {
37+
...accessibilityState,
38+
disabled,
39+
...(isToggle && { checked: selected }),
40+
}
41+
```
42+
43+
The component must own its role and state semantics while preserving unrelated consumer values such as `busy`.
44+
45+
- Set the native role explicitly.
46+
- Keep disabled state, focusability, and interaction behavior consistent.
47+
- Prefer a consumer-provided `accessible` or `focusable` value only when it does not violate the component contract.
48+
- Add selected or checked semantics only when the corresponding behavior is enabled.
49+
- Use action-oriented accessible names for icon-only controls.
50+
51+
Button warns in development when an icon-only instance lacks an `accessibilityLabel`. Put warnings in an effect so
52+
render remains free of observable side effects, and make the dependency list match every value used by the warning.
53+
54+
## Use framework interaction and slot hooks
55+
56+
Interactive roots should use the framework state hook, such as `usePressableState`, so hovered, pressed, and focused
57+
state is normalized and user handlers are forwarded.
58+
59+
Construct slots after native props are resolved:
60+
61+
- `useSlot` for the required root.
62+
- `useOptionalSlot` for optional public slots.
63+
- `useOptionalSlot` for private state-only slots when their condition is active.
64+
65+
Pass `null` for a slot that must not exist. Do not create a placeholder slot and hide it later with styles.
66+
67+
Button constructs its hidden Semibold content and content container only when toggle behavior and visible content require
68+
them. This keeps ordinary buttons free of the extra structure.
69+
70+
## Return stable resolved state
71+
72+
Return:
73+
74+
- slot functions
75+
- required variant values
76+
- derived state
77+
- preserved user styles
78+
- theme state
79+
- interaction state
80+
81+
Keep property ordering intentional so later spreads cannot silently replace component-owned values. If two state sources
82+
can share a key, assign the final owned value explicitly after spreading.
83+
84+
## Platform behavior
85+
86+
Keep platform-specific native imports out of shared files when React Native forks expose incompatible types. Put fork
87+
imports in platform files or redeclare a small platform-neutral contract. Surface unsupported platform behavior rather
88+
than silently pretending it succeeded.
89+
90+
## Review checklist
91+
92+
- Defaults match the spec and depend on already-derived facts.
93+
- Omitted controlled props retain their meaning.
94+
- User native handlers and unrelated accessibility state are preserved.
95+
- Disabled, focusable, and selected semantics agree.
96+
- Icon-only or unlabeled usage is diagnosed consistently.
97+
- Optional slots exist only when their render condition is active.
98+
- The hook contains no token style selection and no JSX rendering.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Styles and tokens
2+
3+
Use this reference for `<component>.styles.ts`, `useApplyStyles.ts`, token mapping, theme caching, state precedence, and
4+
slot prop application. The canonical examples are
5+
[`button.styles.ts`](../../../../packages/agentic-components/src/components/button/button.styles.ts) and
6+
[`useApplyStyles.ts`](../../../../packages/agentic-components/src/components/button/useApplyStyles.ts).
7+
8+
## Resolve values from the correct token source
9+
10+
Consult
11+
[`flex-token-map.yaml`](../../../../packages/agentic-design/src/tokens/mappings/flex-token-map.yaml) before choosing a
12+
value.
13+
14+
- Read semantic colors from `useThemeState().tokens.color`.
15+
- Read grouped categories from `tokens.borderRadius`, `tokens.spacing`, `tokens.strokeWidth`, `tokens.fontFamily`,
16+
`tokens.fontSize`, `tokens.fontWeight`, `tokens.lineHeight`, and `tokens.shadow`.
17+
- Prefer Flex tokens for every mapped semantic value.
18+
- Import named global tokens only when Flex has no equivalent, such as current Button icon dimensions or a true zero.
19+
- Record a genuine token gap in the component spec. Do not replace it with a nearby token or numeric literal.
20+
21+
## Build styles in layers
22+
23+
Use four layers:
24+
25+
1. A module-scoped `StyleSheet` for structural values that do not depend on theme, props, or state.
26+
2. Module-scoped state factories for theme-independent state selectors.
27+
3. Module-scoped themed factories for token-derived styles.
28+
4. Per-instance style arrays that select cached objects and place user styles last.
29+
30+
Never create a style factory inside a hook or render function.
31+
32+
Reuse the package's existing factory utilities:
33+
34+
- [`getStateStyleFactory`](../../../../packages/agentic-components/src/utils/branchedStyle.ts) lazily flattens and caches
35+
one theme-independent state definition.
36+
- [`getThemedStateStyleFactory`](../../../../packages/agentic-components/src/utils/branchedStyle.ts) resolves and caches
37+
one flattened definition per `ThemeState`.
38+
- [`getThemedColorStyleFactory`](../../../../packages/agentic-components/src/utils/colorStyles.ts) converts semantic
39+
color keys and delegates its hierarchy and caching to `getThemedStateStyleFactory`.
40+
41+
These are the canonical Button mechanisms and cache plain resolved style objects in `state.themeStyles`. Do not wrap them
42+
in another `StyleSheet.create` cache. Use
43+
[`themedStyleSheetFactory`](../../../../packages/agentic-design/src/useThemeState.ts) only when a component genuinely
44+
needs a complete theme-only `StyleSheet.create` result that is not represented by a branched state definition.
45+
46+
Every theme factory may depend only on `ThemeState` values such as tokens and high contrast. Props, interaction state,
47+
and user styles must remain outside the cache so one component instance cannot leak into another.
48+
49+
## Declare state hierarchy and precedence
50+
51+
Represent related axes as ordered hierarchy levels. Each level contains mutually exclusive states in priority order.
52+
Button colors use:
53+
54+
```ts
55+
[['primary', 'secondary', 'outline', 'subtle'], ['selected'], ['disabled', 'pressed', 'hovered']];
56+
```
57+
58+
This means appearance is the base branch, selected refines it, and interaction is the final refinement. Put interaction
59+
states in `disabled`, `pressed`, `hovered` order so the first active state has the required priority.
60+
61+
Use `getThemedStateStyleFactory` for token-derived hierarchy definitions and `getStateStyleFactory` for
62+
theme-independent definitions. Use `getThemedColorStyleFactory` for semantic background, border, and foreground colors;
63+
it automatically resolves `tokens.color.hover` and `tokens.color.pressed` for inherited semantic keys.
64+
65+
An interaction may intentionally switch semantic keys. Button Subtle is transparent at rest but explicitly selects
66+
`backgroundNeutralSubtle` inside hovered and pressed branches. Button Outline uses `strokeNeutralLoud`, whose hover and
67+
pressed token values provide visible stroke feedback. Do not assume a token named `Transparent` will produce visible
68+
interaction states; verify the resolved token maps.
69+
70+
Build the state source from resolved state without hiding precedence in conditionals. Button pushes appearance, selected,
71+
disabled, pressed, and hovered values, then lets the declared hierarchy select the winner.
72+
73+
## Group related layout axes
74+
75+
Combine axes that produce one coherent style object. Button selects size, shape, and content layout from one root style
76+
hierarchy:
77+
78+
```ts
79+
[
80+
['small', 'medium', 'large'],
81+
['rounded', 'square', 'circle'],
82+
['withContent', 'iconOnly'],
83+
];
84+
```
85+
86+
Use separate factories for independent concerns such as typography, focus, and content visibility. This keeps unrelated
87+
state branches from multiplying into one large definition.
88+
89+
Destructure only the token groups needed by a factory:
90+
91+
```ts
92+
({ borderRadius, spacing, strokeWidth }: FlexTokens) => ...
93+
```
94+
95+
Validate token values when their generated type is wider than the React Native style property. Button validates its gap
96+
token before assigning it to `ViewStyle['gap']`.
97+
98+
## Apply slot props in one stage
99+
100+
`useApplyStyles.ts` should select styles and call `attachSlotProps`; it should not create factories.
101+
102+
Compose root styles in this order:
103+
104+
1. structural style
105+
2. resolved layout or variant style
106+
3. semantic colors
107+
4. focus or other conditional styles
108+
5. user style
109+
110+
Derive dependent slot props from the same resolved values. Button applies foreground color and size to both icon slots,
111+
marks decorative icons inaccessible, and applies typography plus foreground color to content.
112+
113+
Preserve consumer slot behavior unless the component owns it. Button no longer forces `numberOfLines`; its content and
114+
toggle container use `flexShrink` so constrained labels can wrap. A consumer can still request truncation through the
115+
content slot.
116+
117+
## Selected text without layout shift
118+
119+
When selected text changes weight:
120+
121+
- Render an inaccessible Semibold ghost that reserves width and height.
122+
- Overlay the visible label.
123+
- Keep the ghost and container state-only.
124+
- Apply the same wrapping constraints to both labels.
125+
126+
Button uses theme-independent visibility selectors for the hidden and overlaid styles and token-derived typography for
127+
the actual font metrics.
128+
129+
## Review checklist
130+
131+
- Every value comes from Flex or a documented token gap.
132+
- Structural, themed, instance, and user styles are separated.
133+
- Every factory is module-scoped and theme-safe.
134+
- State hierarchy makes precedence explicit.
135+
- Hover and pressed values are verified, not inferred from token names.
136+
- Independent concerns use independent factories.
137+
- User styles are last.
138+
- Slot props share resolved color and size values consistently.
139+
- Constrained text can wrap unless truncation is an explicit public choice.

0 commit comments

Comments
 (0)