Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/design-taste-frontend/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ source: https://github.com/leonxlnx/taste-skill — skills/taste-skill/SKILL.md
description: Anti-slop frontend skill for landing pages, portfolios, and redesigns. The agent reads the brief, infers the right design direction, and ships interfaces that do not look templated. Real design systems when applicable, audit-first on redesigns, strict pre-flight check.
---

> **In this repo:** Tailwind 3.4 (`apps/sim/tailwind.config.ts`); animation via `import { motion } from 'framer-motion'` (not `motion/react` — rewrite every `motion/react` import in the samples below); icons from `@sim/emcn/icons`; colors through the CSS-variable tokens in `.claude/rules/sim-styling.md` (no hardcoded `text-gray-*`/hex/`zinc` utilities, no paired `dark:` utilities). This note overrides any conflicting guidance or code sample anywhere in this file.
> **In this repo:** Tailwind 4 (CSS-first config in `apps/sim/app/_styles/globals.css`); animation via `import { motion } from 'framer-motion'` (not `motion/react` — rewrite every `motion/react` import in the samples below); icons from `@sim/emcn/icons`; colors through the CSS-variable tokens in `.claude/rules/sim-styling.md` (no hardcoded `text-gray-*`/hex/`zinc` utilities, no paired `dark:` utilities). This note overrides any conflicting guidance or code sample anywhere in this file.
Comment thread
waleedlatif1 marked this conversation as resolved.

# tasteskill: Anti-Slop Frontend Skill

Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/sim-settings-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Adding a new settings page:
## Text-scale tokens (no literal pixel sizes)

Settings pages never use a literal `text-[Npx]` class — always the named Tailwind
scale token from `apps/sim/tailwind.config.ts`'s `fontSize` extension (`text-micro`
scale token from the `@theme` block in `apps/sim/app/_styles/globals.css` (`text-micro`
10px, `text-xs` 11px, `text-caption` 12px, `text-small` 13px, `text-sm` 14px
[Tailwind default, unmodified], `text-base` 15px, `text-md` 16px, `text-lg` 18px
[Tailwind default]). A literal size is either a straight rename to the equivalent
Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/sim-styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ setWidth: (width) => {

## Text Scale

Custom font sizes (`apps/sim/tailwind.config.ts`): `text-micro`=10px, `text-xs`=11px, `text-caption`=12px, `text-small`=13px, `text-base`=15px. `text-sm` is Tailwind default 14px. Field titles use `text-small` (13px); hints/errors use `text-caption` (12px).
Custom font sizes (the `@theme` block in `apps/sim/app/_styles/globals.css`): `text-micro`=10px, `text-xs`=11px, `text-caption`=12px, `text-small`=13px, `text-base`=15px. `text-sm` is Tailwind default 14px. Field titles use `text-small` (13px); hints/errors use `text-caption` (12px).

Icons default `size-[14px]`. Equal h/w → `size-*` (`size-[14px]`, `size-4`), never `h-N w-N`.

Expand Down
2 changes: 1 addition & 1 deletion .cursor/rules/sim-styling.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ setWidth: (width) => {

## Text Scale

Custom font sizes (`apps/sim/tailwind.config.ts`): `text-micro`=10px, `text-xs`=11px, `text-caption`=12px, `text-small`=13px, `text-base`=15px. `text-sm` is Tailwind default 14px. Field titles use `text-small` (13px); hints/errors use `text-caption` (12px).
Custom font sizes (the `@theme` block in `apps/sim/app/_styles/globals.css`): `text-micro`=10px, `text-xs`=11px, `text-caption`=12px, `text-small`=13px, `text-base`=15px. `text-sm` is Tailwind default 14px. Field titles use `text-small` (13px); hints/errors use `text-caption` (12px).

Icons default `size-[14px]`. Equal h/w → `size-*` (`size-[14px]`, `size-4`), never `h-N w-N`.

Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,9 @@ __pycache__/
# `apps/sim/lib/uploads/` — 61 files of tracked source — and silently ignore
# anything added there later.
/uploads

# Scratch dir for `check:cn-tables` (regenerates the cn lookup tables to a
# throwaway path and diffs them against the committed copy). Anchored to the one
# directory the script actually creates, so an unrelated `.cn-check` elsewhere
# is not silently hidden.
/packages/emcn/.cn-check/
6 changes: 3 additions & 3 deletions apps/docs/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

/* Every @sim/emcn component expresses hover through `hover-hover:` so touch
devices never latch a sticky hover state. The app registers it as a plugin
variant in apps/sim/tailwind.config.ts; docs is CSS-first Tailwind v4 with no
variant in apps/sim/app/_styles/globals.css; docs is CSS-first Tailwind v4 with no
config, so without this declaration the variant compiles to nothing and every
emcn hover state silently no-ops here. */
@custom-variant hover-hover {
Expand Down Expand Up @@ -35,7 +35,7 @@ body {
--color-fd-primary: var(--color-fd-foreground);
/* Sim's custom type scale — emcn components (Label, Badge, the shared block
views) use these names, so they must resolve here or text falls back to the
inherited size. Mirrors apps/sim/tailwind.config.ts. */
inherited size. Mirrors apps/sim/app/_styles/globals.css. */
--text-micro: 10px;
--text-xs: 11px;
--text-caption: 12px;
Expand All @@ -47,7 +47,7 @@ body {
--text-caption and --text-small, because the mono face reads small at 12px. */
--text-code: 0.78125rem;

/* The platform's code face. `apps/sim/tailwind.config.ts` lists `var(--font-martian-mono)`
/* The platform's code face. `apps/sim/app/_styles/globals.css` lists `var(--font-martian-mono)`
first, but nothing in the app ever defines that variable — `apps/sim/app/layout.tsx`
applies only `season.variable` — so every code surface in the product resolves to the
system stack below. Docs match what the app actually renders rather than the token it
Expand Down
17 changes: 10 additions & 7 deletions apps/docs/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'

/**
* Combines multiple class names into a single string, merging Tailwind classes properly
* Re-exported from the design system rather than built here, so docs runs one
* merge engine instead of two: its own components already import `cn` from
* `@sim/emcn` indirectly, and a second `cn` entry point would ship a duplicate
* engine and lookup tables.
*
* It also carries emcn's `font-size` class group, which docs needs — its
* `global.css` defines the same `--text-micro/caption/small/md` scale, and the
* stock merger does not treat those as font sizes, so `cn('text-small',
* 'text-sm')` emitted both and let CSS source order pick the winner.
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export { cn } from '@sim/emcn'

/**
* Get the full URL for an asset stored in Vercel Blob
Expand Down
2 changes: 0 additions & 2 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"@sim/workflow-renderer": "workspace:*",
"@xyflow/react": "12.11.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"drizzle-orm": "^0.45.2",
"fumadocs-core": "16.8.5",
"fumadocs-mdx": "14.3.2",
Expand All @@ -35,7 +34,6 @@
"react-dom": "19.2.4",
"remark-breaks": "^4.0.0",
"shiki": "4.3.1",
"tailwind-merge": "^3.0.2",
"framer-motion": "^12.5.0",
"zod": "4.3.6"
},
Expand Down
6 changes: 3 additions & 3 deletions apps/sim/app/(interfaces)/chat/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const ChatInput: React.FC<{
const canSubmit = (inputValue.trim().length > 0 || attachedFiles.length > 0) && !isStreaming

return (
<div className='fixed right-0 bottom-0 left-0 flex w-full items-center justify-center bg-gradient-to-t from-[var(--bg)] to-transparent px-4 pb-4 md:px-0 md:pb-4'>
<div className='fixed right-0 bottom-0 left-0 flex w-full items-center justify-center bg-linear-to-t from-[var(--bg)] to-transparent px-4 pb-4 md:px-0 md:pb-4'>
<div className='w-full max-w-3xl md:max-w-[748px]'>
{uploadErrors.length > 0 && (
<div className='mb-3 flex flex-col gap-2'>
Expand Down Expand Up @@ -171,7 +171,7 @@ export const ChatInput: React.FC<{
{attachedFiles.map((file) => (
<Tooltip.Root key={file.id}>
<Tooltip.Trigger asChild>
<div className='group relative size-[56px] flex-shrink-0 cursor-pointer overflow-hidden rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-3)]'>
<div className='group relative size-[56px] shrink-0 cursor-pointer overflow-hidden rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-3)]'>
{file.dataUrl ? (
<img
src={file.dataUrl}
Expand Down Expand Up @@ -214,7 +214,7 @@ export const ChatInput: React.FC<{
onKeyDown={handleKeyDown}
placeholder={isDragOver ? 'Drop files here...' : 'Enter a message...'}
rows={1}
className='m-0 h-auto min-h-[24px] w-full resize-none overflow-y-auto overflow-x-hidden border-0 bg-transparent p-1 text-[15px] text-[var(--text-primary)] leading-[24px] caret-[var(--text-primary)] outline-none [-ms-overflow-style:none] [scrollbar-width:none] placeholder:text-[var(--text-muted)] focus-visible:ring-0 focus-visible:ring-offset-0 [&::-webkit-scrollbar]:hidden'
className='m-0 h-auto min-h-[24px] w-full resize-none overflow-y-auto overflow-x-hidden border-0 bg-transparent p-1 text-[15px] text-[var(--text-primary)] leading-[24px] caret-[var(--text-primary)] outline-hidden [-ms-overflow-style:none] [scrollbar-width:none] placeholder:text-[var(--text-muted)] focus-visible:ring-0 focus-visible:ring-offset-0 [&::-webkit-scrollbar]:hidden'
/>

<div className='flex items-center justify-between'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function ChatMessageContainer({
</div>

{showScrollButton && (
<div className='-translate-x-1/2 absolute bottom-16 left-1/2 z-20 transform'>
<div className='-translate-x-1/2 absolute bottom-16 left-1/2 z-20'>
<Button
onClick={scrollToBottom}
size='sm'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ export function ChatFileDownload({ file }: ChatFileDownloadProps) {
disabled={isDownloading}
className='group flex h-auto w-[200px] items-center gap-2 rounded-lg px-3 py-2'
>
<div className='flex size-8 flex-shrink-0 items-center justify-center'>{renderIcon()}</div>
<div className='flex size-8 shrink-0 items-center justify-center'>{renderIcon()}</div>
<div className='min-w-0 flex-1 text-left'>
<div className='w-[100px] truncate text-xs'>{file.name}</div>
<div className='text-[var(--text-muted)] text-micro'>{formatFileSize(file.size)}</div>
</div>
<div className='flex-shrink-0'>
<div className='shrink-0'>
{isDownloading ? (
<Loader className='size-3.5' animate />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export const ClientChatMessage = memo(function ClientChatMessage({
/>
) : (
<>
<div className='flex size-10 flex-shrink-0 items-center justify-center rounded bg-[var(--surface-3)] md:size-12'>
<div className='flex size-10 shrink-0 items-center justify-center rounded bg-[var(--surface-3)] md:size-12'>
{getFileIcon(attachment.type)}
</div>
<div className='min-w-0 flex-1'>
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/app/(landing)/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The landing page looks like the product. Its visual language is the workspace UI
- **Always light.** The root wrapper in `landing.tsx` carries the `light` class, which pins every token to its light value (see `app/_styles/globals.css`, the `:root, .light` block). Never add `dark:` variants here; never read the user's theme.
- **Use platform tokens, never hex.** Canvas `--bg`, surfaces `--surface-1`…`--surface-7`, cards/modals `--surface-2`, hover `--surface-hover`, active `--surface-active`; text `--text-primary` / `--text-secondary` / `--text-muted` / `--text-body`, icons `--text-icon`; borders `--border` (dividers) / `--border-1` (fields); brand `--brand-agent` / `--brand-secondary` / `--brand-accent`. Do **not** use the legacy `--landing-*` tokens - they belong to the old dark landing.
- **Use emcn components where they fit.** The chip family (`Chip`, `ChipLink`, `ChipTag`, `ChipInput`, `ChipModal*`, …) from `@/components/emcn` is the canonical chrome - a demo-request form is a `ChipModal` with `ChipModalField`s, a pill CTA is a `Chip`/`ChipLink`. Components own their chrome; pass props, not className overrides. Full consumer rules: `.claude/rules/sim-styling.md`.
- **Typography is the platform's.** Season is the global body font (`font-season` is applied on `<body>` in the root layout). Use the platform text scale (`text-small` = 13px, `text-base` = 15px, etc. - see `tailwind.config.ts`). Don't add new fonts or font CSS variables without explicit direction.
- **Typography is the platform's.** Season is the global body font (`font-season` is applied on `<body>` in the root layout). Use the platform text scale (`text-small` = 13px, `text-base` = 15px, etc. - see the `@theme` block in `app/_styles/globals.css`). Don't add new fonts or font CSS variables without explicit direction.
- **Never touch global styles.** No additions to `app/_styles/globals.css`. All styling is local Tailwind classes; `cn()` from `@/lib/core/utils/cn` for conditionals; no inline `style` attributes.
- **Responsive - desktop is the source of truth, scaled down via `max-*` overrides.** The page is fully responsive (iPad + phone). The desktop layout stays the unprefixed baseline; smaller screens are handled by *layering* `max-*` overrides on top, so desktop renders byte-identically. Tiers:
- `max-xl:` (≤1279) - the hero's two-panel split (absolute visual + logos) collapses to a stacked, in-flow column. The split needs ≥1280 to avoid the headline colliding with the visual panel; iPad-landscape (1024) therefore gets the stacked hero with the desktop nav.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export function ContentAuthorPage({
timeZone: 'UTC',
})}
</span>
<h3 className='text-[var(--text-primary)] text-base leading-tight tracking-[-0.01em] lg:text-lg'>
<h3 className='text-[var(--text-primary)] text-base leading-tight tracking-[-0.01em] lg:text-lg lg:leading-7'>
{p.title}
</h3>
<p className='line-clamp-2 text-[var(--text-muted)] text-sm leading-[150%]'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export function ContentIndexPage({
timeZone: 'UTC',
})}
</span>
<h3 className='text-[var(--text-primary)] text-base leading-tight tracking-[-0.01em] lg:text-lg'>
<h3 className='text-[var(--text-primary)] text-base leading-tight tracking-[-0.01em] lg:text-lg lg:leading-7'>
{p.title}
</h3>
<p className='line-clamp-2 text-[var(--text-muted)] text-sm leading-[150%]'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function ContentPostLoading() {
<Skeleton className='h-[16px] w-[100px] rounded-[4px] bg-[var(--surface-hover)]' />
</div>
<div className='flex flex-col gap-8 md:flex-row md:gap-12'>
<div className='w-full flex-shrink-0 md:w-[450px]'>
<div className='w-full shrink-0 md:w-[450px]'>
<Skeleton className='aspect-[450/360] w-full rounded-[5px] bg-[var(--surface-hover)]' />
</div>
<div className='flex flex-1 flex-col justify-between'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function ContentPostPage({
</div>

<div className='flex flex-col gap-8 md:flex-row md:gap-12'>
<div className='w-full flex-shrink-0 md:w-[450px]'>
<div className='w-full shrink-0 md:w-[450px]'>
<div className='relative w-full overflow-hidden rounded-[5px]'>
<Image
src={post.ogImage}
Expand All @@ -79,7 +79,7 @@ export function ContentPostPage({
{post.title}
</h1>
<p
className='mt-4 text-[var(--text-body)] text-base leading-[150%] tracking-[0.02em] sm:text-lg'
className='mt-4 text-[var(--text-body)] text-base leading-[150%] tracking-[0.02em] sm:text-lg sm:leading-7'

@cubic-dev-ai cubic-dev-ai Bot Sep 2, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: At sm and wider, sm:leading-7 overrides the existing leading-[150%], changing the 18px description from 27px to 28px line-height. Remove this override or use the previous 150% value to preserve the stated visual parity.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/(landing)/components/content-post-page/content-post-page.tsx, line 82:

<comment>At `sm` and wider, `sm:leading-7` overrides the existing `leading-[150%]`, changing the 18px description from 27px to 28px line-height. Remove this override or use the previous 150% value to preserve the stated visual parity.</comment>

<file context>
@@ -79,7 +79,7 @@ export function ContentPostPage({
               </h1>
               <p
-                className='mt-4 text-[var(--text-body)] text-base leading-[150%] tracking-[0.02em] sm:text-lg'
+                className='mt-4 text-[var(--text-body)] text-base leading-[150%] tracking-[0.02em] sm:text-lg sm:leading-7'
                 itemProp='description'
               >
</file context>
Suggested change
className='mt-4 text-[var(--text-body)] text-base leading-[150%] tracking-[0.02em] sm:text-lg sm:leading-7'
className='mt-4 text-[var(--text-body)] text-base leading-[150%] tracking-[0.02em] sm:text-lg'
Fix with cubic

itemProp='description'
>
{post.description}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export function HeroChatLoop({ phase, fading }: HeroChatLoopProps) {
i > 0 && 'border-t'
)}
>
<span className='flex size-[16px] flex-shrink-0 items-center justify-center'>
<span className='flex size-[16px] shrink-0 items-center justify-center'>
<span className='text-[var(--text-icon)] text-sm'>{i + 1}</span>
</span>
<span className='flex-1 text-[var(--text-body)] text-sm'>{title}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface StageBlockCardProps {
*/
export function StageBlockCard({ block }: StageBlockCardProps) {
return (
<div className='relative rounded-[13px] border border-[var(--border-1)] bg-[var(--surface-2)] shadow-sm'>
<div className='relative rounded-[13px] border border-[var(--border-1)] bg-[var(--surface-2)] shadow-xs'>
<WorkflowBlockContent block={block} />
{!block.isTrigger && (
<span
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export function StageHome({
// The chat input's aesthetic (rounded-2xl, border, soft shadow) is
// kept THROUGHOUT - including once morphed into the workflow block -
// so it stays the same white card, just resized.
'relative mx-auto overflow-hidden rounded-2xl border border-[var(--border-1)] bg-[var(--surface-2)] shadow-sm',
'relative mx-auto overflow-hidden rounded-2xl border border-[var(--border-1)] bg-[var(--surface-2)] shadow-xs',
// While the height is held (the parent-driven send-bubble grow, and the
// loader slide where the card keeps its size), do NOT CSS-transition
// height - a transition would fight the per-frame camera/dock tracking
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ export function KnowledgeBasePanel({
)}
style={{ '--drop-delay': `${120 + i * 170}ms` } as CSSProperties}
>
<file.icon className='size-[14px] flex-shrink-0 text-[var(--text-icon)]' />
<file.icon className='size-[14px] shrink-0 text-[var(--text-icon)]' />
<span className='min-w-0 flex-1 truncate text-[14px] text-[var(--text-body)]'>
{file.name}
</span>
<span className='flex-shrink-0 text-[14px] text-[var(--text-muted)]'>
<span className='shrink-0 text-[14px] text-[var(--text-muted)]'>
{file.size}
</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function WorkflowBlockContent({ block }: WorkflowBlockContentProps) {
>
<div
className={cn(
'flex size-[24px] flex-shrink-0 items-center justify-center rounded-md',
'flex size-[24px] shrink-0 items-center justify-center rounded-md',
block.tileBorder && 'border border-[var(--border-1)]'
)}
style={{ background: block.bgColor }}
Expand All @@ -40,11 +40,9 @@ export function WorkflowBlockContent({ block }: WorkflowBlockContentProps) {
<div className='flex flex-col gap-2 p-2'>
{block.rows.map((row) => (
<div key={row.title} className='flex items-center gap-2'>
<span className='flex-shrink-0 text-[14px] text-[var(--text-muted)]'>
{row.title}
</span>
<span className='shrink-0 text-[14px] text-[var(--text-muted)]'>{row.title}</span>
<span className='flex min-w-0 flex-1 items-center justify-end gap-1.5 text-[14px] text-[var(--text-body)]'>
{row.valueIcon && <row.valueIcon className='size-[14px] flex-shrink-0' />}
{row.valueIcon && <row.valueIcon className='size-[14px] shrink-0' />}
<span className='truncate'>{row.value}</span>
</span>
</div>
Expand Down
Loading
Loading