返回 Skill 列表
extension
分类: 开发与工程无需 API Key

canvas-styling-conventions

使用Tailwind CSS 4主题令牌和已批准的实用模式来样式化Canvas组件。在以下情况下使用:(1) 创建新组件,(2) 为组件添加颜色或样式,(3) 使用Tailwind主题令牌,(4) 在global.css中添加或更新设计令牌,(5) 创建颜色/样式属性,(6) 任何添加/删除/重命名/重新定义组件属性且可能影响渲染样式的更改。涵盖@theme变量、CVA变体和cn()实用程序。

person作者: jakexiaohubgithub

Project type

Before applying this skill, check package.json for a dependency named @drupal-canvas/headless or starting with @drupal-canvas/headless-. If one is present, this is a Canvas Headless codebase — read canvas-headless first; it overrides the React-specific guidance below. The Tailwind 4 theme-token guidance in this skill applies to headless projects unchanged; the cn() and className sections are React-specific. If no such dependency is present, this is a Canvas-rendered React codebase: components are React (index.jsx/.tsx) and everything in this skill applies as written. These are the only two project types.

Technology stack

| Technology | Purpose | | ------------------------------------ | ------------------ | | Tailwind CSS 4.1+ | Styling | | class-variance-authority (CVA) | Component variants | | clsx + tailwind-merge via cn() | Class name merging |

Only use these dependencies for styling. Do not add third-party CSS libraries or create new styling utilities.

In a Canvas Headless codebase, the drupal-canvas package (and its cn()) is not supported. CVA still works in any framework when the project installs it; for class merging, use the framework's own class-composition idiom — see canvas-headless.

Styling conventions

  • Use Tailwind's theme colors (primary-*, gray-*) defined in global.css.
  • Avoid hardcoded color values; use theme tokens instead.
  • Follow the existing focus, hover, and active state patterns from examples.

The cn() utility

Use cn() to merge Tailwind classes. It combines clsx for conditional classes with tailwind-merge to resolve conflicting utilities. Import from either source:

import { cn } from '@/lib/utils';
// or
import { cn } from 'drupal-canvas';

Example usage:

const Button = ({ variant, className, children }) => (
  <button
    className={cn(
      'rounded px-4 py-2',
      variant === 'primary' && 'bg-primary-600 text-white',
      variant === 'secondary' && 'bg-gray-200 text-gray-800',
      className,
    )}
  >
    {children}
  </button>
);

Accept className for style customization

This rule is React-specific; in Vue and Astro components, rely on the framework's own class pass-through (attribute fallthrough in Vue, a class prop in Astro) when consumers need style overrides.

Every React component should accept a className prop to allow style overrides. Pass it to cn() as the last argument so consumer classes take precedence.

const Card = ({ colorScheme, className, children }) => (
  <div className={cn(cardVariants({ colorScheme }), className)}>{children}</div>
);

className is an implementation/composition prop, not an editor prop. Do not add className to component.yml, do not mark it as required, and do not surface it in Canvas metadata.

Tailwind 4 theme variables

Canvas projects use Tailwind CSS 4's @theme directive to define design tokens in global.css. Variables defined inside @theme { } automatically become available as Tailwind utility classes.

Always check global.css for available design tokens. The @theme block is the source of truth for colors, fonts, breakpoints, and other design tokens.

How theme variables map to utility classes

When you define a CSS variable in @theme, Tailwind 4 automatically generates corresponding utility classes based on the variable's namespace prefix:

| CSS Variable in @theme | Generated Utility Classes | | --------------------------- | ---------------------------------------------------------- | | --color-primary-600: #xxx | bg-primary-600, text-primary-600, border-primary-600 | | --color-gray-100: #xxx | bg-gray-100, text-gray-100, border-gray-100 | | --font-sans: ... | font-sans | | --breakpoint-md: 48rem | md: responsive prefix |

The pattern is: --{namespace}-{name} becomes {utility}-{name}.

Examples

Given this definition in global.css:

@theme {
  --color-primary-600: #1899cb;
  --color-primary-700: #1487b4;
}

You can use these colors with any color-accepting utility:

// Correct
<button className="bg-primary-600 hover:bg-primary-700 text-white">
  Click me
</button>

// Wrong
<button className="bg-[#1899cb] text-white hover:bg-[#1487b4]">Click me</button>

Arbitrary values (e.g., bg-[#xxx]) are acceptable for rare, one-off cases where adding a theme variable would be overkill. However, if a color appears in multiple places or represents a brand/design system value, add it to @theme instead.

Semantic aliases

Theme variables can reference other variables to create semantic aliases:

@theme {
  --color-primary-700: #1487b4;
  --color-primary-dark: var(--color-primary-700);
}

Both bg-primary-700 and bg-primary-dark will work. Use semantic aliases when they better express intent (e.g., primary-dark for a darker brand variant).

Adding or updating theme variables

When a design requires a color, font, or other value not yet defined in the theme, add it to the @theme block in global.css rather than hardcoding the value in a component.

When to add new theme variables:

  • A design introduces a new brand color or shade
  • You need a semantic alias for an existing value (e.g., --color-accent)
  • The design uses a specific spacing, font, or breakpoint value repeatedly

When to update existing theme variables:

  • The brand colors change (update the hex values)
  • Design tokens need adjustment across the system

Example - adding a new color:

@theme {
  /* Existing tokens */
  --color-primary-600: #1899cb;

  /* New token for a success state */
  --color-success: #22c55e;
  --color-success-dark: #16a34a;
}

After adding, you can immediately use bg-success, text-success-dark, etc.

Keep the theme organized. Group related tokens together with comments explaining their purpose. Follow the existing naming conventions in global.css (e.g., numbered shades like primary-100 through primary-900, semantic names like primary-dark).

Color props must use variants, not color codes

Never create props that allow users to pass color codes (hex values, RGB, HSL, or any raw color strings). Instead, define a small set of human-readable variants using CVA that map to the design tokens in global.css.

Always check global.css for available design tokens. The tokens defined there (such as primary-*, gray-*, etc.) are the source of truth for color values.

Wrong - allowing raw color values:

# Wrong
props:
  properties:
    backgroundColor:
      title: Background Color
      type: string
      examples:
        - '#3b82f6'
// Wrong
const Card = ({ backgroundColor }) => (
  <div style={{ backgroundColor }}>{/* ... */}</div>
);

Correct - using CVA variants with design tokens:

# Correct
props:
  properties:
    colorScheme:
      title: Color Scheme
      type: string
      enum:
        - default
        - primary
        - muted
        - dark
      meta:enum:
        default: Default (White)
        primary: Primary (Blue)
        muted: Muted (Light Gray)
        dark: Dark
      examples:
        - default
// Correct
import { cva } from 'class-variance-authority';

const cardVariants = cva('rounded-lg p-6', {
  variants: {
    colorScheme: {
      default: 'bg-white text-black',
      primary: 'bg-primary-600 text-white',
      muted: 'bg-gray-100 text-gray-700',
      dark: 'bg-gray-900 text-white',
    },
  },
  defaultVariants: {
    colorScheme: 'default',
  },
});

const Card = ({ colorScheme, children }) => (
  <div className={cardVariants({ colorScheme })}>{children}</div>
);

This approach ensures:

  • Consistent colors across the design system
  • Users select from curated, meaningful options (not arbitrary values)
  • Easy theme updates by modifying global.css tokens
  • Better accessibility through tested color combinations