
Building Ui Bundle Frontend
- 1.5k installs
- 763 repo stars
- Updated July 24, 2026
- forcedotcom/afv-library
This is a copy of building-ui-bundle-frontend by forcedotcom - installs and ranking accrue to the original listing.
building-ui-bundle-frontend is a Claude Code skill from the AFV library that generates React, Tailwind, and shadcn UI components following a strict design system for developers who need consistent frontend bundles withou
About
building-ui-bundle-frontend is a Claude Code skill from forcedotcom/afv-library for implementing UI bundle frontend components. It mandates shadcn components from `@/components/ui` for buttons, inputs, cards, alerts, tabs, tables, and labels—never raw HTML equivalents. All styling uses Tailwind utility classes and design tokens like `bg-background`, `text-foreground`, and `text-muted-foreground`; conditional classes use `cn()` from `@/lib/utils`. Components are TypeScript functional modules with typed props and optional `className`. Use building-ui-bundle-frontend when generating AFV UI that must match the design system on first pass.
- Always uses shadcn components from @/components/ui instead of raw HTML
- Enforces Tailwind utility classes and design tokens exclusively
- Requires cn() utility for conditional class names
- Mandates TypeScript functional components with typed props including optional className
- Defines exact file locations and export rules for shared, feature, and layout components
Building Ui Bundle Frontend by the numbers
- 1,504 all-time installs (skills.sh)
- +1 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/forcedotcom/afv-library --skill building-ui-bundle-frontendAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.5k |
|---|---|
| repo stars | ★ 763 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 24, 2026 |
| Repository | forcedotcom/afv-library ↗ |
How do you build shadcn React components with design tokens?
Generate consistent, high-quality React + Tailwind UI components that follow a strict design system without manual style debugging.
Who is it for?
Frontend developers on AFV or shadcn-plus-Tailwind codebases who want agents to follow strict component and token conventions.
Skip if: Projects using CSS Modules, inline styles, raw HTML form controls, or non-React UI frameworks outside the AFV design system.
When should I use this skill?
The user builds UI bundle frontend components and needs shadcn, Tailwind tokens, and TypeScript props enforced without style drift.
What you get
React TypeScript components in `@/components/ui` patterns with Tailwind design tokens and typed props.
- React TypeScript UI components
- Tailwind-styled shadcn implementations
Files
UI Bundle UI
Identify the Task
Determine which category the request falls into:
| Category | Examples | Implementation Guide |
|---|---|---|
| Page | New routed page (contacts, dashboard, settings) | implementation/page.md |
| Header / Footer | Site-wide nav bar, footer, branding | implementation/header-footer.md |
| Component | Widget, card, table, form, dialog | implementation/component.md |
---
Layout and Navigation
appLayout.tsx is the source of truth for navigation and layout. Every page shares this shell.
When making any change that affects navigation, header, footer, sidebar, theme, or layout:
1. Edit src/appLayout.tsx — the layout used by routes.tsx 2. Replace all default/template nav items and labels with app-specific links and names 3. Replace placeholder app name everywhere: header, nav brand, footer, <title> in index.html
Before finishing, confirm: Did I update appLayout.tsx with real nav items and branding?
| What | Where |
|---|---|
| Layout, nav, branding | src/appLayout.tsx |
| Document title | index.html |
| Root page content | Component at root route in routes.tsx |
---
React and TypeScript Standards
Routing
Use a single router package. With createBrowserRouter / RouterProvider, all imports must come from react-router (not react-router-dom).
If the app uses a client-side router (React Router, Remix Router, Vue Router, etc.), always derive basename / basepath / base from the document's <base href> tag at runtime. Never hardcode the basename:
const basename = document.querySelector('base')
? new URL(document.querySelector('base').href).pathname.replace(/\/$/, '')
: '/';
const router = createBrowserRouter(routes, { basename });Component Library and Styling
- shadcn/ui for components:
import { Button } from '@/components/ui/button'; - Tailwind CSS utility classes
URL and Path Handling
Apps run behind dynamic base paths. Router navigation (<Link to>, navigate()) uses absolute paths (/x). Non-router attributes (<img src>) use dot-relative (./x). Prefer Vite import for static assets.
TypeScript
- Never use
any— use proper types, generics, orunknownwith type guards - Event handlers:
(event: React.FormEvent<HTMLFormElement>): void - State:
useState<User | null>(null)— always provide the type parameter - No unsafe assertions (
obj as User) — use type guards instead
Module Restrictions
React UI bundles must not import Salesforce platform modules like lightning/* or @wire (LWC-only). For data access, use the using-ui-bundle-salesforce-data skill.
---
Design Thinking
Before coding, commit to a bold aesthetic direction:
- Purpose: What problem does this interface solve? Who uses it?
- Tone: Pick a clear direction — brutally minimal, maximalist, retro-futuristic, organic, luxury, playful, editorial, brutalist, art deco, soft/pastel, industrial. Use these as inspiration but design one true to the context.
- Differentiation: What makes this unforgettable? What's the one thing someone will remember?
Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work — the key is intentionality, not intensity.
---
Frontend Aesthetics
- Typography: Choose distinctive, characterful fonts. Pair a display font with a refined body font. Never default to Inter, Roboto, Arial, Space Grotesk, or system fonts.
- Color: Commit to a cohesive palette using CSS variables. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. Avoid cliched purple gradients on white.
- Motion: Focus on high-impact moments — one well-orchestrated page load with staggered reveals (
animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise. Prefer CSS-only solutions; use Motion library for React when available. - Spatial Composition: Unexpected layouts — asymmetry, overlap, diagonal flow, grid-breaking elements. Generous negative space OR controlled density.
- Backgrounds & Depth: Create atmosphere rather than defaulting to solid colors. Gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, grain overlays.
- Mobile Responsiveness: All generated UI MUST be mobile-responsive. Use Tailwind responsive prefixes (
sm:,md:,lg:) to adapt layouts across breakpoints. Stack columns on small screens, use flexible grids, and ensure touch targets are at least 44px. Test that navigation, typography, and spacing work on mobile viewports.
Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate animations and effects. Minimalist designs need restraint, precision, and careful spacing/typography. No two designs should look the same — vary themes, fonts, and aesthetics across generations.
---
Clarifying Questions
Ask one question at a time and stop when you have enough context.
For a Page
1. Name and purpose? 2. URL path? 3. Should it appear in navigation? 4. Access control? (public, authenticated via PrivateRoute, or unauthenticated via AuthenticationRoute) 5. Content sections? (list, form, table, detail view) 6. Data fetching needs?
For a Header / Footer
1. Header, footer, or both? 2. Contents? (logo, nav links, user avatar, copyright, social icons) 3. Sticky header? 4. Color scheme or style direction?
For a Component
1. What should it do? 2. Which page does it belong to? 3. Shared/reusable or specific to one feature? 4. Data or props needed? 5. Internal state? (loading, toggle, form state) 6. Specific shadcn components to use?
---
Verification
Before completing, run lint and build from the UI bundle directory. Lint must result in 0 errors and build must succeed.
Implementation — Component
Rules
1. Always use shadcn components from @/components/ui — never build raw HTML equivalents for buttons, inputs, cards, alerts, tabs, tables, or labels. 2. All styling via Tailwind — utility classes only. No inline style={{}}, CSS Modules, or other styling systems. 3. Use design tokens — prefer bg-background, text-foreground, text-muted-foreground, border, bg-primary, text-destructive, rounded-lg over hardcoded colors. 4. Use `cn()` from @/lib/utils for conditional or composable class names. 5. TypeScript — functional components with typed props interface; always accept className?: string.
File Location — Component
| Component type | Location | Export |
|---|---|---|
| Shared UI primitive (reusable across features) | src/components/ui/ — add to index.ts | Named export |
| Feature-specific (e.g., dashboard widget) | src/components/<feature>/ | Named export, import directly where used |
| Page-level layout element | src/components/layout/ | Named export |
Component Structure
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui";
import { cn } from "@/lib/utils";
interface MyComponentProps {
title: string;
value: string;
className?: string;
}
export function MyComponent({ title, value, className }: MyComponentProps) {
return (
<Card className={cn("border", className)}>
<CardHeader>
<CardTitle className="text-sm font-medium">{title}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-2xl font-semibold text-foreground">{value}</p>
</CardContent>
</Card>
);
}State and Hooks
- Local state only: keep
useState,useReducer,useRefinside the component. - Shared or complex state: extract to a custom hook in
src/hooks/(prefix withuse, e.g.useFormData). Do this when more than one component needs the state, or when multiple hooks are composed together.
Adding the Component to a Page
// In the target page file, e.g. src/pages/HomePage.tsx
import { MyComponent } from "@/components/<feature>/MyComponent";
export default function HomePage() {
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<MyComponent title="Status" value="Active" />
</div>
);
}Useful Patterns — Component
- Programmatic navigation: use
useNavigatefromreact-router; callnavigate(path)— consistent with GlobalSearchInput, SearchResultCard, MaintenanceTable, and other components in the UI bundle. - Page container:
max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 - Icons:
lucide-react; addaria-hidden="true"on decorative icons - Focus styles: use
focus-visible:variants - Multiple visual variants: use CVA (
cva) andVariantProps - shadcn import barrel:
import { Button, Card, Input } from "@/components/ui"
Confirm — Component
- Imports use path aliases (
@/, not deep relative paths) - No raw
<button>,<input>, or styled<div>where shadcn equivalents exist - No inline
style={{}}— Tailwind only
Implementation — Header / Footer
Rules
1. Edit `appLayout.tsx` only — header and footer are layout-level concerns. Never add them to individual page files. 2. Never modify `routes.tsx` or `app.tsx` — the router setup must remain intact. 3. Create component files in `src/components/layout/` — the designated location for layout-level components. 4. Use the full-height flex column pattern — wrap layout in min-h-screen flex flex-col so footer stays at bottom. 5. Use shadcn and Tailwind — compose from @/components/ui; style with Tailwind utility classes and design tokens. 6. Use path aliases — import with @/components/layout/... and @/components/ui; no deep relative paths. 7. Preserve existing content — if appLayout.tsx already has a <NavigationMenu /> or other shell elements, keep them in place.
Step 1 — Create the header component (if requested)
Create src/components/layout/AppHeader.tsx:
import { cn } from "@/lib/utils";
interface AppHeaderProps {
className?: string;
}
export function AppHeader({ className }: AppHeaderProps) {
return (
<header
className={cn(
"w-full border-b bg-background px-4 sm:px-6 lg:px-8 py-4",
className,
)}
>
<div className="max-w-7xl mx-auto flex items-center justify-between">
<span className="text-lg font-semibold text-foreground">My App</span>
</div>
</header>
);
}Step 2 — Create the footer component (if requested)
Create src/components/layout/AppFooter.tsx:
import { cn } from "@/lib/utils";
interface AppFooterProps {
className?: string;
}
export function AppFooter({ className }: AppFooterProps) {
return (
<footer
className={cn(
"w-full border-t bg-background px-4 sm:px-6 lg:px-8 py-4",
className,
)}
>
<div className="max-w-7xl mx-auto text-center text-sm text-muted-foreground">
© {new Date().getFullYear()} My App. All rights reserved.
</div>
</footer>
);
}Step 3 — Edit appLayout.tsx
Open src/appLayout.tsx — this is the only file to modify for layout-level additions. Wrap existing content in a flex column and add header above and footer below <Outlet />:
import { Outlet } from "react-router";
import { AppHeader } from "@/components/layout/AppHeader";
import { AppFooter } from "@/components/layout/AppFooter";
// Keep all existing imports unchanged
export default function AppLayout() {
return (
<div className="min-h-screen flex flex-col bg-background">
<AppHeader />
{/* Keep any existing NavigationMenu or other shell elements here */}
<main className="flex-1">
<Outlet />
</main>
<AppFooter />
</div>
);
}File Locations — Header / Footer
| Component | File | Export |
|---|---|---|
| Header | src/components/layout/AppHeader.tsx | Named export |
| Footer | src/components/layout/AppFooter.tsx | Named export |
| Layout shell | src/appLayout.tsx | Default export (edit in place) |
Why appLayout.tsx — Not Pages or Routes
AppLayout is the single shell rendered at the root route. Every page is a child rendered via <Outlet />. Placing the header and footer here ensures they appear on every page without touching individual pages or the route registry.
AppLayout (appLayout.tsx)
├── AppHeader ← renders on every page
├── NavigationMenu ← keep if already present
├── <Outlet /> ← active page renders here
└── AppFooter ← renders on every pageUseful Patterns — Header / Footer
- Sticky header: add
sticky top-0 z-50to the<header>element - Separator: use
<Separator />from@/components/uiinstead ofborder-b/border-tif a visible divider is preferred - Nav links in header: use
<Button variant="ghost" asChild>wrapping a React Router<Link> - Icons:
lucide-react; addaria-hidden="true"on decorative icons - Design tokens:
bg-background,text-foreground,text-muted-foreground,border,bg-primary
Mobile hamburger / Menu icon — Must be functional
If the header includes a hamburger or Menu icon for mobile:
- Do not add a Menu/hamburger icon that does nothing. It must toggle a visible mobile menu.
- Required: (1) State:
const [isOpen, setIsOpen] = useState(false). (2) Button:onClick={() => setIsOpen(!isOpen)},aria-label="Toggle menu". (3) Conditional panel:{isOpen && ( <div>...nav links...</div> )}with responsive visibility (e.g.md:hidden). (4) Close on navigate: each link in the panel shouldonClick={() => setIsOpen(false)}. - Implement in
appLayout.tsx(or the component that owns the header). Use theMenuicon fromlucide-react.
Confirm — Header / Footer
- Header and footer appear on every page (navigate to at least two routes)
- Imports use path aliases (
@/components/layout/...) - No inline
style={{}}— Tailwind only src/routes.tsxandsrc/app.tsxare unchanged
Implementation — Page
Rules
1. Edit the component that owns the UI, never output raw HTML — When editing the home page or any page content, modify the actual .tsx file that renders the target. If the target is inside a child component (e.g. <GlobalSearchInput /> in Home.tsx), edit the child's file (e.g. GlobalSearchInput.tsx), not the parent. Do not wrap the component with extra elements in the parent; go into the component and change its JSX. Do not paste or generate raw HTML. 2. `routes.tsx` is the only route registry — never add routes in app.tsx or inside page files. 3. All pages are children of the AppLayout route — do not create top-level routes that bypass the layout shell. 4. Default export per page — each page file has exactly one default-export component. 5. Path aliases in all imports — use @/pages/..., @/components/...; no deep relative paths. 6. No inline styles — Tailwind utility classes and design tokens only. 7. Catch-all last — path: '*' (NotFound) must always remain the last child in the layout route. 8. Never modify `appLayout.tsx` when adding a page — layout changes are a separate concern.
Step 1 — Create the page file
Create src/pages/MyPage.tsx with a default export and the standard page container:
export default function MyPage() {
return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<h1 className="text-3xl font-bold text-foreground">My Page</h1>
<p className="mt-4 text-muted-foreground">Page content goes here.</p>
</div>
);
}Use shadcn components from @/components/ui for UI elements. All styling via Tailwind — no inline style={{}}.
Step 2 — Register the route in routes.tsx
Open src/routes.tsx. Import the page and add it inside the layout route's children array:
import MyPage from "@/pages/MyPage";
// Inside the layout route's children array (before the catch-all):
{
path: "my-page",
element: <MyPage />,
handle: { showInNavigation: true, label: "My Page" },
},pathis a relative segment (e.g.,"contacts"), not an absolute path.- Include
handle: { showInNavigation: true, label: "Label" }only if the page should appear in the navigation menu. - The catch-all
path: '*'must stay last.
Step 3 — Apply an auth guard (if needed)
| Access type | Guard | Behavior |
|---|---|---|
| Public | None | Direct child of layout |
| Authenticated only | <PrivateRoute> | Redirects to login if not authenticated |
| Unauthenticated only (e.g., login) | <AuthenticationRoute> | Redirects away if already authenticated |
Example — private page:
import { PrivateRoute } from "@/components/auth/private-route";
{
path: "settings",
element: <PrivateRoute><SettingsPage /></PrivateRoute>,
handle: { showInNavigation: true, label: "Settings" },
},Use ROUTES.* constants from @/utils/authenticationConfig for auth-related paths — do not hardcode /login, /profile, etc.
File Conventions — Page
| Concern | Location |
|---|---|
| Page component | src/pages/<PageName>.tsx (default export) |
| Route definition | src/routes.tsx only |
| Layout shell | src/appLayout.tsx — do not modify for page additions |
| Auth config paths | ROUTES.* from @/utils/authenticationConfig |
State and Data
- Local state:
useState,useReducer,useRefinside the page component - Shared or complex state: extract to
src/hooks/with auseprefix (e.g.,useContacts) - Data fetching: prefer GraphQL (
executeGraphQL) or REST utilities insrc/api/; place shared data logic insrc/hooks/ - Auth context:
useAuth()from@/context/AuthContextwhen current user is needed — only valid underAuthProvider
Confirm — Page
- The page renders inside the app shell (header/nav visible)
- If
showInNavigation: true, the link appears in the navigation menu - No TypeScript errors; no broken imports; no missing exports
- Imports use path aliases (
@/, not deep relative paths)
Related skills
FAQ
Can building-ui-bundle-frontend use inline styles?
building-ui-bundle-frontend forbids inline `style={{}}`, CSS Modules, and non-Tailwind styling. All visual styling must use Tailwind utility classes and design tokens such as `bg-primary` and `text-destructive`.
Which component library does building-ui-bundle-frontend require?
building-ui-bundle-frontend always imports shadcn components from `@/components/ui` for buttons, inputs, cards, alerts, tabs, tables, and labels. Raw HTML equivalents for those primitives are not allowed.
Is Building Ui Bundle Frontend safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.