ParameterPanel
StableA set of choices for making something with AI, with a live cost and a Generate button.
Also known as settings panel, generation settings, controls panel
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Video settings with cost
const params: ParameterSpec[] = [
{ kind: "segmented", id: "duration", label: "Duration", options: [{ value: "5", label: "5 s" }, { value: "10", label: "10 s" }] },
{ kind: "aspect", id: "ratio", label: "Shape", options: ["16:9", "9:16", "1:1", "4:3"].map((r) => ({ value: r, label: r })) },
{ kind: "seed", id: "seed", label: "Repeat code", defaultSeed: 4821 },
];
const [values, setValues] = useState({ duration: "5", ratio: "16:9", seed: null });
<ParameterPanel parameters={params} values={values} onChange={setValues}
onGenerate={() => {}} estimateCost={(v) => Number(v.duration) * 12} />Length, shape and a repeat code. The cost changes when the length changes.
When to use it
Gathers the settings for one thing being made, with the cost shown above Generate. People can change settings while they can still see what it will cost.
Made of: Title · Settings · Cost · Generate button
Use it for
- Settings for one image, video or other result, next to where the request is typed.
- Showing a cost that updates as the settings change.
- Letting people fix a repeat code so they can get similar results again.
Not for
- Settings that apply to the whole app.
- Actions on a result that already exists: use VariationActions.
- Choosing which AI model to use: use ModelPicker.
Do and avoid
Do
- Show the cost before making anything, so people can change settings first.
- Keep the list short. Put the settings people change most at the top.
- Add a hint on the repeat code that says what it does and does not promise.
- Work out the cost from the same settings that Generate uses.
- Set busy while the result is being made, so Generate turns off and shows it is working.
Avoid
- Do not use amber for the cost. Cost is just information until it blocks something.
- Do not bury the Generate button below a long list of settings.
- Do not keep the settings inside the panel. Your app owns them.
- Do not use it for settings that apply to the whole app. It is for one creation.
On a phone
- The panel fills the width, and the option chips wrap onto new lines.
- Switches and number boxes grow to 44px tall on a touch screen, and the number boxes are 16px so the page does not zoom. Chips keep their size and have a 44px tap area.
- Option descriptions are set as a title only, which a finger cannot show, so put anything people must read in the label or hint.
- The cost line and Generate button stay on one row at the bottom.
Seen in products
- Runway Gen-4 offers duration of 5 or 10 seconds and aspect ratios 16:9, 9:16, 1:1, 4:3, 3:4 and 21:9.
- Runway has a fixed-seed toggle and a seed number for repeatability. The seed is documented as similar, not identical.
- Runway shows the credit cost per second before generating. Gen-4 is 12 credits per second and Turbo is 5.
- Midjourney has Stylize and aspect ratio controls.
- Shape of AI lists Parameters, Preset styles, Saved styles, Prompt enhancer and Voice and tone under Tuners, and Cost estimates and Draft mode under Governors.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| parametersrequired | ParameterSpec[] | none | Schema in display order. Each entry has an id, a label, an optional hint and a kind of segmented, aspect, presets, number or seed. |
| valuesrequired | Record<string, string | number | boolean | null> | none | Current value for each parameter id. A seed is a number when Fixed is on and null when it is off. |
| onChangerequired | (values: ParameterValues) => void | none | Called with a new values object whenever one control changes. |
| onGeneraterequired | (values: ParameterValues) => void | none | Called with the current values when the form is submitted. Not called while busy. |
| estimateCost | (values: ParameterValues) => number | none | Returns the cost for the current values. Without it the readout is empty. |
| formatCost | (cost: number) => string | `About ${cost} credits` | Formats the readout text. |
| title | string | "Settings" | Heading, also the accessible name of the form. |
| generateLabel | string | "Generate" | Text on the submit button. |
| busyLabel | string | "Generating…" | Button text while busy. |
| busy | boolean | none | Disables Generate and shows a spinner. |
| className | string | none | Extra classes for the form, for width. |
"Show code under the preview gives the code for the example on the stage."
In Figma
In Figma, one ParameterPanel switches all of this from its properties panel, across 2 variants. Each property sets the React prop beside it, so design and code use the same names.
| Figma property | React | Values |
|---|---|---|
| Busy | busy | On or off |
Tokens
Change one of these and every part that uses it follows.
--surface--surface-sunken--border--lime--on-lime--fg-muted--shadow-sm--radius-control--radius-field--radius-card
Built in
- The panel is a form named by its title, and Generate submits it, so Enter in a number box generates.
- Each group of options is named by its label. Arrow keys move and choose, and Tab stops only on the chosen option.
- Shape options say the ratio in words. The small drawing beside them is hidden from screen readers.
- The Fixed switch and the number boxes are named from their labels.
- Screen readers hear the new cost when it changes, without losing their place.
What you need to do
- Pass estimateCost when there is a cost, so screen readers hear it change.
- Write labels that make sense on their own. Each control is named from its label, and the hint is not read with it.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide