Skill: UI Translation
Purpose
Translate natural language UI descriptions into IDS (Iress Design System) component implementations using @iress-oss/ids-components and @iress-oss/ids-tokens.
Translation Workflow
- Identify the UI elements — Break the description into components: actions (buttons), inputs (fields), layout (stacks, grids), content (text, cards), overlays (modals, slideouts), navigation
- Map to IDS components — Use the component mapping to find the right IDS component for each element
- Verify component capabilities — Before recommending a component, read its
.ai/components/<name>.mddoc (innode_modules/@iress-oss/ids-components/.ai/components/) to verify it supports the required features (async, filtering, validation, etc.) - Apply layout — Wrap elements in
IressStack(vertical),IressInline(horizontal), orIressRow/IressCol(grid) only when needed. Check whether the parent component already provides layout (e.g. card footer, modal actions, button group) before adding wrappers. Never wrap a single child in a layout component. Always make grids responsive withspan={{xs: 12, md:...}} - Add responsive behaviour — Even if the description only mentions desktop, stack columns on mobile and relocate secondary content to
IressSlideoutor collapsible sections - Apply styling — Use styling props for spacing, colour, and typography. Spacing tokens must include the category prefix:
gap="spacing.4",p="spacing.6". Alias tokens ("xs","sm","md","lg","xl") are also valid. Never use bare numbers likegap="4". - Verify output — Check that all imports resolve, no raw HTML is used where IDS components exist, grid layouts use responsive
spanvalues, and no common anti-patterns are present (disabled buttons, slot attributes, redundant textStyle)
Setup
Important: IDS v6 is currently in beta. Install with the@betatag: ``bash npm install @iress-oss/ids-components@beta # If using tokens directly: npm install @iress-oss/ids-tokens@beta``
import '@iress-oss/ids-components/dist/style.css'; // Required — component styles
import '@iress-oss/ids-tokens/build/css-vars.css'; // Required if using tokens directly
import {
IressProvider,
IressButton,
IressInput,
IressField,
IressStack,
IressInline,
IressText,
IressCard,
// ... import what you need
} from '@iress-oss/ids-components';
// Wrap your app in IressProvider (handles fonts + CSS variables)
function App() {
return <IressProvider>{/* your UI */}</IressProvider>;
}Component Mapping
When you need to find the right IDS component for a UI element, read references/component-mapping.md for the full description → IDS component mapping tables (actions, form inputs, layout, content, overlays, navigation, tables).
Styling Props
When you need to apply spacing, colour, visibility, or typography props, read references/styling-props.md for the full IressCSSProps reference and accepted values.
Responsive Layout
Always produce responsive output. Even when the UI description only mentions a desktop layout, every translation should adapt to smaller screens. IDS uses a 12-column grid with 6 breakpoints (xs, sm, md, lg, xl, xxl).
Responsive Design Principles
Apply these when translating any UI description:
- Identify the primary task — What is the user trying to accomplish? The mobile layout should focus on this task and push everything else to secondary access points.
- Stack multi-column layouts — Side-by-side columns should stack to full-width on mobile (
span={{xs: 12, md:...}}). - Relocate secondary content — Move supplementary UI (filters, sidebars, metadata panels, secondary actions) into an
IressSlideout,IressModal, or collapsible section on mobile. - Simplify dense layouts — For tables with many columns or multi-panel dashboards, hide non-essential columns with
hideBelow, collapse sections, or switch to a card-based layout usinguseBreakpoint. - Preserve all functionality — Never remove features on mobile. Use
IressSlideout,IressModal, expandable sections, orIressTabSetto keep functionality accessible without cluttering the view.
Responsive Props
Many props accept a single value or an object keyed by breakpoint:
// Full-width on mobile, half on medium+
<IressCol span={{ xs: 12, md: 6 }} />
// Tighter gap on mobile, larger on desktop
<IressRow gutter={{ xs: 'sm', md: 'lg' }} />Props that support responsive values: span, offset, gap, gutter, rowGap, p, px, py, m, mx, my, width, and all directional margin/padding props.
Responsive Visibility
Use hideFrom/hideBelow CSS props directly on any component:
<IressButton hideBelow="md">Desktop action</IressButton>
<IressText hideFrom="lg">Mobile only text</IressText>For conditional rendering based on breakpoint (e.g. rendering entirely different components), use the useBreakpoint hook:
import { useBreakpoint } from '@iress-oss/ids-components';
function Navigation() {
const { breakpoint } = useBreakpoint();
const isMobile = breakpoint === 'xs' || breakpoint === 'sm';
return isMobile ? <MobileNav /> : <DesktopNav />;
}Translation Examples
"A login form with email and password fields and a submit button"
import {
IressButton,
IressField,
IressInput,
IressStack,
} from '@iress-oss/ids-components';
function LoginForm() {
return (
<IressStack gap="md">
<IressField label="Email" htmlFor="email" required>
<IressInput id="email" type="email" placeholder="Enter your email" />
</IressField>
<IressField label="Password" htmlFor="password" required>
<IressInput
id="password"
type="password"
placeholder="Enter your password"
/>
</IressField>
<IressButton mode="primary" type="submit">
Log in
</IressButton>
</IressStack>
);
}"A card with a title, description, and two action buttons"
import { IressCard, IressButton, IressInline } from '@iress-oss/ids-components';
function ActionCard() {
return (
<IressCard
heading={<h3>Card Title</h3>}
footer={
<IressInline gap="sm">
<IressButton mode="primary">Confirm</IressButton>
<IressButton mode="secondary">Cancel</IressButton>
</IressInline>
}
>
This is the card description with supporting details.
</IressCard>
);
}"A settings page with a toggle, some checkboxes, and a save button"
import {
IressStack,
IressToggle,
IressCheckboxGroup,
IressCheckbox,
IressButton,
IressText,
IressDivider,
IressField,
} from '@iress-oss/ids-components';
function SettingsPage() {
return (
<IressStack gap="lg">
<IressText element="h2">Settings</IressText>
<IressToggle>Enable notifications</IressToggle>
<IressDivider />
<IressField label="Notification types">
<IressCheckboxGroup name="notification-types">
<IressCheckbox value="email">Email</IressCheckbox>
<IressCheckbox value="sms">SMS</IressCheckbox>
<IressCheckbox value="push">Push</IressCheckbox>
</IressCheckboxGroup>
</IressField>
<IressDivider />
<IressButton mode="primary">Save settings</IressButton>
</IressStack>
);
}"A dashboard with a sidebar and main content area"
Note: even though the description doesn't mention mobile, the sidebar is secondary content — on mobile it should move into a slideout so the main content gets focus.
import { useState } from 'react';
import {
IressRow,
IressCol,
IressStack,
IressText,
IressCard,
IressButton,
IressSlideout,
useBreakpoint,
} from '@iress-oss/ids-components';
function Dashboard() {
const { breakpoint } = useBreakpoint();
const isMobile = breakpoint === 'xs' || breakpoint === 'sm';
const [navOpen, setNavOpen] = useState(false);
const nav = (
<IressCard heading={<h3>Navigation</h3>}>
Menu items here
</IressCard>
);
return isMobile ? (
<IressStack gap="md">
<IressButton
mode="secondary"
icon="menu"
onClick={() => setNavOpen(true)}
>
Menu
</IressButton>
<IressCard>
<IressText element="h2">Main Content</IressText>
</IressCard>
<IressSlideout
heading="Navigation"
show={navOpen}
onShowChange={setNavOpen}
>
{nav}
</IressSlideout>
</IressStack>
) : (
<IressRow gutter="lg">
<IressCol span={3}>{nav}</IressCol>
<IressCol span={9}>
<IressCard>
<IressText element="h2">Main Content</IressText>
</IressCard>
</IressCol>
</IressRow>
);
}Best Practices
- Minimise component nesting — Use the fewest components possible to achieve the layout. Every wrapper component should earn its place. Before adding
IressInlineorIressStack, check whether the parent component already handles the layout (e.g.IressCardhasheadingandfooterprops;IressModalhasactions;IressButtonGrouphandles horizontal button layout). See the "Unnecessary layout wrappers" section in Common Mistakes below. - Always wrap in IressProvider — Required at the root of your app for fonts and CSS variables.
IressProvideralready includesIressModalProvider,IressSlideoutProvider,IressToasterProvider, andIressIconProvider— do not add these separately. If usingIressShadow, no additional providers are needed as it includesIressProviderinternally. - Use IressField for all form inputs — Provides consistent labels, hints, and validation display
- Use IressStack/IressInline only when needed — Prefer these over custom CSS flex/grid, but don't add them when the parent already provides spacing or layout
- Use correct spacing token format — Always prefix with the token category:
gap="spacing.4",p="spacing.6". Alias tokens ("xs","sm","md","lg","xl") are also valid. Never use bare numbers likegap="4". - Use semantic button modes — One
primaryper section,secondaryfor most actions - Always include labels — All form inputs need accessible labels via
IressField - Use status for feedback —
IressAlertfor inline messages,IressModal status="danger"for confirmation dialogs,statusprop on buttons for danger/success - Prefer IDS components — Use
IressTextinstead of raw<p>,IressButtoninstead of<button> - Native elements inside
IressTextare OK — When rendering CMS content, markdown output, or other unstructured data sources, it is acceptable to nest native HTML elements (e.g.<p>,<strong>,<a>,<ul>) insideIressText. This letsIressTextprovide consistent typography while allowing flexible inner content structure. - Always make grid layouts responsive — When using
IressRow/IressCol, use responsivespanvalues (e.g.span={{xs: 12, md: 6}}) so columns stack on mobile instead of overflowing - Check the component docs — Read the specific component doc for detailed props and patterns (
node_modules/@iress-oss/ids-components/.ai/components/)
Common Mistakes
Unnecessary layout wrappers
The most common mistake is wrapping children in IressInline or IressStack when it adds no value. Every wrapper must serve a purpose — if removing it produces the same result, remove it.
// ❌ Unnecessary nesting — IressStack wrapping a single child
<IressStack gap="md">
<IressInline gap="sm">
<IressButton mode="primary">Save</IressButton>
<IressButton mode="secondary">Cancel</IressButton>
</IressInline>
</IressStack>
// ✅ Single group of buttons only needs IressInline
<IressInline gap="sm">
<IressButton mode="primary">Save</IressButton>
<IressButton mode="secondary">Cancel</IressButton>
</IressInline>// ❌ Wrapping content that's already a single block
<IressStack gap="md">
<IressText element="h2">Title</IressText>
</IressStack>
// ✅ No wrapper needed for a single element
<IressText element="h2">Title</IressText>When to use layout wrappers:
IressStack— when you have 2+ block-level siblings that need vertical spacing between themIressInline— when you have 2+ elements that need to sit side-by-side (e.g. buttons in a cardfooter, action bars)
When NOT to use them:
- The parent component already handles layout (modal
actionsprop, button group) - There's only one child — a wrapper around a single child adds nothing
- You're nesting
IressInlineinsideIressInlineorIressStackinsideIressStackwithout changing gap/alignment — flatten instead
Other common anti-patterns
For the full list of common anti-patterns (disabled buttons, redundant textStyle, legacy slot attributes, raw HTML, hardcoded values), read the Common Mistakes guide at node_modules/@iress-oss/ids-components/.ai/guides/foundations-common-mistakes.md (requires @iress-oss/ids-components to be installed).