From montage-web-guide
Builds UI components, pages, and styles with Montage (Wanted Design System) in React/Next.js using MCP server for specs, tokens, icons, and guidelines. Triggers on UI tasks in @wanteddev/wds projects.
npx claudepluginhub wanteddev/montage-web --plugin montage-web-guideThis skill uses the workspace's default tool permissions.
Skill that is automatically applied when developing components based on Wanted Design System (Montage) in React projects.
Provides CLI lookups for Wix Design System (@wix/design-system) components, props, examples, testkits, and icons. Use when selecting React UI components, checking APIs, or writing tests with drivers for unidriver, puppeteer, or playwright.
Generates design systems with tokens, atomic components, WCAG accessibility, theming, and docs. Provides React templates for consistent, scalable UI.
Generates distinctive, production-grade frontend UI components with bold designs and working code. Use for hero sections, pricing cards, PRDs, or API contracts.
Share bugs, ideas, or general feedback.
Skill that is automatically applied when developing components based on Wanted Design System (Montage) in React projects.
Apply this skill when any of the following conditions are met:
@wanteddev/wds, @wanteddev/wds-icon, @wanteddev/wds-dummy, @wanteddev/wds-brandBefore using any MCP tools, always verify the montage-mcp-server connection first.
Call mcp__montage-mcp-server__health_check to check the connection status.
/mcp to connect montage-mcp-server./mcp will initiate the auth flow.If the connection check fails, do not proceed. Wait for the user's response.
Gather required information in parallel at the start. Avoid unnecessary sequential calls.
When setting up React.js or Next.js from scratch, use:
mcp__montage-mcp-server__getting_startedAlways call in parallel:
mcp__montage-mcp-server__wds_coding_guidelines — coding guidelinesmcp__montage-mcp-server__list_components — available component listAdd in parallel when needed:
mcp__montage-mcp-server__list_tokens — when custom styling is requiredmcp__montage-mcp-server__get_color_usage — when color application is neededmcp__montage-mcp-server__list_icons — when icons are neededmcp__montage-mcp-server__list_dummy_components — when a placeholder GNB / Footer / bottom tab bar is needed for a demo or previewmcp__montage-mcp-server__list_brand_assets — when the Wanted brand logo (or other brand mark) is neededWhen using Montage components, never guess — always look up the detailed specs.
mcp__montage-mcp-server__get_component({ componentName: "ComponentName" })
Important: Looking up the parent component instead of a sub-component gives you the full composition pattern (Anatomy) and all APIs at once.
get_component({ componentName: "Modal" }) — includes ModalContainer, ModalNavigation, etc.get_component({ componentName: "Card" }) — includes CardThumbnail, CardContent, etc.get_component({ componentName: "Tab" }) — includes TabList, TabListItem, TabPanel, etc.When unsure which Typography variant to use, call get_component({ componentName: "Typography" }) to check the size table for each variant.
Follow these patterns for vibe design or page implementation requests.
Basic page skeleton:
import { Box, FlexBox, containerStyle } from '@wanteddev/wds';
<FlexBox flexDirection="column" sx={{ minHeight: '100vh' }}>
<Box as="header" sx={containerStyle(true)}>
...
</Box>
<Box as="main" sx={containerStyle(true)}>
...
</Box>
<Box as="footer" sx={containerStyle(true)}>
...
</Box>
</FlexBox>;
For
containerStyledetails, useget_utility_function("containerStyle").
| Purpose | Component |
|---|---|
| One-directional layout (row/column) | FlexBox |
| 12-column grid layout | Grid + GridItem |
| Page container | containerStyle() utility |
| General wrapper/styling | Box |
When implementing a Figma design directly, ignore this guide and use the exact spacing values defined in Figma.
Otherwise (vibe design or freeform composition), do not use spacing tokens. Use px values directly instead.
gap="12px" or gap="16px"gap="24px" ~ gap="32px"gap="40px" ~ gap="48px"gap prop or specify padding, margin in px via sxThree methods are available. Pick based on what's changing:
sm={{ size: "large" }})respondTo / respondDown utilitiesuseMediaQuery hookUse mobile-first sizing, then override with sm, md, lg, xl. Typically only sm breakpoint is used.
TextButton, IconButton (variant background, normal), ListCell, RadioGroupItem, Checkbox, AvatarButton, ToggleIcon have interaction areas larger than their visual bounds — give slightly more spacing room.ScrollArea with border-radius, add [data-role='scroll-area-bar-wrapper'] { padding-block: ${radius}; } to avoid unnatural scrollbar.sx propWhen using a theme function in the sx prop, the theme is injected automatically. No need to pass it manually like
wrapperStyle(theme).
// style.ts
export const buttonStyle = (isActive: boolean) => (theme: Theme) => css`
color: ${isActive
? theme.semantic.primary.normal
: theme.semantic.label.alternative};
`;
// index.tsx
<Box sx={buttonStyle(isActive)} />;
When icons are needed, check the Montage icon library first via mcp__montage-mcp-server__list_icons. Always prefer Montage icons over creating new ones.
Use utility functions provided by Montage. Look up available utilities via mcp__montage-mcp-server__list_utility_functions, and get detailed usage with mcp__montage-mcp-server__get_utility_function.
Use Montage design tokens instead of hardcoded values. Look up available tokens via mcp__montage-mcp-server__list_tokens.
Never use CSS variable (var(--semantic-...)) directly. Always access colors through the theme callback (e.g., sx={theme => ({ color: theme.semantic.label.normal })}). CSS variable names are internal implementation details and may change without notice.
Colors: use semantic color tokens instead of #RRGGBB (fall back to atomic colors if not possible). Use get_color_usage to look up which token to use for a given purpose.
Typography: use the Typography component or typographyStyle utility. Use get_component({ componentName: "Typography" }) to look up the variant/size table.
Shadows: use theme.semantic.elevation.shadow.normal.*
Opacity: must use addOpacity utility + theme.opacity[N]. Theme color values are CSS variables (e.g. var(--semantic-primary-normal)), so appending hex alpha strings directly will NOT work. Available opacity keys: 0, 5, 8, 12, 16, 22, 28, 35, 43, 52, 61, 74, 88, 97, 100.
// use addOpacity utility
import { addOpacity } from '@wanteddev/wds';
import type { Theme } from '@wanteddev/wds';
const wrapperStyle = (theme: Theme) => css`
background-color: addOpacity(
theme.semantic.primary.normal,
theme.opacity[5]
);
border: 1px solid
${addOpacity(theme.semantic.primary.normal, theme.opacity[22])};
`;
When implementing a Figma design:
If Figma specifies an opacity value that does not match any theme.opacity[N] key, use the raw number directly (e.g., opacity: 0.07) instead of a token.
If the Figma design renders the opacity as a colored overlay layer (not fading the element itself), implement it via ::before / ::after pseudo-element so child content stays fully opaque. When the parent has border-radius, the pseudo-element must also set borderRadius: 'inherit', otherwise the overlay will overflow the rounded corners.
// style.ts
import { css } from '@wanteddev/wds';
import type { Theme } from '@wanteddev/wds';
export const overlayStyle = (theme: Theme) => css`
position: relative;
border-radius: 12px;
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
background-color: ${theme.semantic.fill.normal};
opacity: ${theme.opacity[8]};
}
`;
// index.tsx
import { overlayStyle } from './style';
<Box sx={overlayStyle} />;
Do not use spacing tokens. Use px values directly.
@wanteddev/wds-dummy)The @wanteddev/wds-dummy package provides presentational-only reference scaffolds (NavBar, Footer, BottomTabBar) that mirror the wanted.co.kr layout.
onClick, no routing, no state). For real product code, copy the structure and rebuild it against your own data, links, and handlers using primitives from @wanteddev/wds.forwardRef components and accept a standard sx prop. BottomTabBar is intended for viewports below the sm breakpoint — wrap it accordingly if you also render NavBar on the same page.mcp__montage-mcp-server__list_dummy_components. To see the exact JSX (e.g. to copy and adapt), read the source from packages/wds-dummy/src/components/<component>/index.tsx.import { NavBar, Footer, BottomTabBar } from '@wanteddev/wds-dummy';
<>
<NavBar />
<main>{/* page content */}</main>
<Footer />
<BottomTabBar />
</>;
Official Wanted brand marks. Use as-is — do not recolor, restyle, redraw, or crop.
LogoWanted (@wanteddev/wds-brand)Use when you need the "[symbol] wanted" wordmark (symbol + text together) — e.g. GNB, footer, splash, marketing surfaces.
width (default 112), height (default 32), plus standard sx and any <svg> attributes. Forwards ref to SVGSVGElement.aria-label (e.g. "Wanted Logo") when the logo stands alone in an interactive or landmark element.112:32 (≈ 3.5:1) aspect ratio when resizing.import { LogoWanted } from '@wanteddev/wds-brand';
<LogoWanted aria-label="Wanted Logo" />
<LogoWanted width={168} height={48} aria-label="Wanted Logo" />;
IconSymbol (@wanteddev/wds-icon)Use when you need just the Wanted symbol mark (no wordmark) — e.g. favicons, app icons, compact headers, loading indicators, badges. Renders as a 1:1 square.
fontSize (defaults to 1em). Built-in <title> provides accessible name "원티드 심벌".LogoWanted to fake a symbol-only mark — always use IconSymbol.import { IconSymbol } from '@wanteddev/wds-icon';
<IconSymbol sx={{ fontSize: '32px' }} />;
Look up the full list of brand assets via mcp__montage-mcp-server__list_brand_assets. Prefer these over importing or recreating any other Wanted logo SVG.
Verify the following after completing a component/page:
get_component before using components? (no guessing)@wanteddev/wds-dummy scaffolds instead of hand-rolling a fake GNB/Footer/tab bar?LogoWanted from @wanteddev/wds-brand (not a custom SVG)?IconSymbol from @wanteddev/wds-icon (not a cropped LogoWanted)?