原子设计模板Skill atomic-design-templates

原子设计模板技能用于在前端开发中,基于原子设计方法论创建可重用的页面布局模板。它涉及定义内容区域、实现响应式设计、组合组件以提高开发效率和一致性。关键词包括原子设计、模板、页面布局、前端开发、React、CSS、响应式设计、组件组合。

前端开发 0 次安装 2 次浏览 更新于 3/25/2026

名称:原子设计模板 描述:用于在没有真实内容的情况下创建页面布局。模板使用生物体、分子和原子定义页面的骨架结构。 允许工具:

  • Bash
  • 读取
  • 写入
  • 编辑
  • Glob
  • Grep

原子设计:模板

掌握模板的创建 - 页面级布局,定义内容结构而无需实际内容。模板建立页面将使用的骨架结构。

什么是模板?

模板是页面级对象,将组件放置到布局中,并阐明设计的基础内容结构。它们是:

  • 由生物体组成:将生物体排列到页面布局中
  • 内容无关:使用占位内容,而非真实数据
  • 结构性:定义内容类型将出现的位置
  • 可重用:同一模板可用于多个页面
  • 响应式:处理所有视口大小

常见模板类型

营销模板

  • 落地页面布局
  • 首页布局
  • 产品展示布局
  • 关于/公司布局

应用模板

  • 仪表板布局
  • 设置页面布局
  • 个人资料页面布局
  • 列表/详情页面布局

内容模板

  • 博客文章布局
  • 文章布局
  • 文档布局
  • 帮助中心布局

电子商务模板

  • 产品列表布局
  • 产品详情布局
  • 结账布局
  • 订单确认布局

MainLayout 模板示例

完整实现

// templates/MainLayout/MainLayout.tsx
import React from 'react';
import { Header, type HeaderProps } from '@/components/organisms/Header';
import { Footer, type FooterProps } from '@/components/organisms/Footer';
import styles from './MainLayout.module.css';

export interface MainLayoutProps {
  /** 头部配置 */
  headerProps: HeaderProps;
  /** 底部配置 */
  footerProps: FooterProps;
  /** 主要内容 */
  children: React.ReactNode;
  /** 显示面包屑 */
  showBreadcrumbs?: boolean;
  /** 面包屑组件 */
  breadcrumbs?: React.ReactNode;
  /** 最大内容宽度 */
  maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
  /** 页面背景颜色 */
  background?: 'white' | 'gray' | 'primary';
}

export const MainLayout: React.FC<MainLayoutProps> = ({
  headerProps,
  footerProps,
  children,
  showBreadcrumbs = false,
  breadcrumbs,
  maxWidth = 'lg',
  background = 'white',
}) => {
  return (
    <div className={`${styles.layout} ${styles[`bg-${background}`]}`}>
      <Header {...headerProps} />

      <main className={styles.main}>
        {showBreadcrumbs && breadcrumbs && (
          <div className={styles.breadcrumbs}>{breadcrumbs}</div>
        )}

        <div className={`${styles.content} ${styles[`max-${maxWidth}`]}`}>
          {children}
        </div>
      </main>

      <Footer {...footerProps} />
    </div>
  );
};

MainLayout.displayName = 'MainLayout';
/* templates/MainLayout/MainLayout.module.css */
.layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.main {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.breadcrumbs {
  padding: 16px 24px;
  background-color: var(--color-neutral-50);
  border-bottom: 1px solid var(--color-neutral-200);
}

.content {
  flex: 1;
  margin: 0 auto;
  padding: 24px;
  width: 100%;
}

/* 最大宽度变体 */
.max-sm {
  max-width: 640px;
}

.max-md {
  max-width: 768px;
}

.max-lg {
  max-width: 1024px;
}

.max-xl {
  max-width: 1280px;
}

.max-full {
  max-width: 100%;
}

/* 背景变体 */
.bg-white {
  background-color: var(--color-white);
}

.bg-gray {
  background-color: var(--color-neutral-50);
}

.bg-primary {
  background-color: var(--color-primary-50);
}

/* 响应式调整 */
@media (max-width: 768px) {
  .content {
    padding: 16px;
  }
}

DashboardLayout 模板示例

// templates/DashboardLayout/DashboardLayout.tsx
import React, { useState } from 'react';
import { Header } from '@/components/organisms/Header';
import { Sidebar, type SidebarProps } from '@/components/organisms/Sidebar';
import styles from './DashboardLayout.module.css';

export interface DashboardLayoutProps {
  /** 头部属性 */
  headerProps: {
    logo: React.ReactNode;
    user?: { name: string; email: string; avatar?: string };
    onLogout?: () => void;
  };
  /** 侧边栏属性 */
  sidebarProps: SidebarProps;
  /** 主要内容 */
  children: React.ReactNode;
  /** 页面标题 */
  pageTitle?: string;
  /** 页面描述 */
  pageDescription?: string;
  /** 页面操作(按钮等) */
  pageActions?: React.ReactNode;
  /** 侧边栏初始折叠 */
  sidebarCollapsed?: boolean;
}

export const DashboardLayout: React.FC<DashboardLayoutProps> = ({
  headerProps,
  sidebarProps,
  children,
  pageTitle,
  pageDescription,
  pageActions,
  sidebarCollapsed = false,
}) => {
  const [isCollapsed, setIsCollapsed] = useState(sidebarCollapsed);

  return (
    <div className={styles.layout}>
      {/* 顶部头部 */}
      <Header
        logo={headerProps.logo}
        navigation={[]}
        user={headerProps.user}
        onLogout={headerProps.onLogout}
        showSearch={false}
      />

      <div className={styles.body}>
        {/* 侧边栏 */}
        <Sidebar
          {...sidebarProps}
          isCollapsed={isCollapsed}
          onToggleCollapse={() => setIsCollapsed(!isCollapsed)}
        />

        {/* 主要内容区域 */}
        <main className={styles.main}>
          {/* 页面头部 */}
          {(pageTitle || pageActions) && (
            <header className={styles.pageHeader}>
              <div className={styles.titleSection}>
                {pageTitle && <h1 className={styles.pageTitle}>{pageTitle}</h1>}
                {pageDescription && (
                  <p className={styles.pageDescription}>{pageDescription}</p>
                )}
              </div>
              {pageActions && (
                <div className={styles.pageActions}>{pageActions}</div>
              )}
            </header>
          )}

          {/* 页面内容 */}
          <div className={styles.content}>{children}</div>
        </main>
      </div>
    </div>
  );
};

DashboardLayout.displayName = 'DashboardLayout';
/* templates/DashboardLayout/DashboardLayout.module.css */
.layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.body {
  display: flex;
  flex: 1;
}

.main {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
  background-color: var(--color-neutral-50);
}

.pageHeader {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 24px;
  padding: 24px;
  background-color: var(--color-white);
  border-bottom: 1px solid var(--color-neutral-200);
}

.titleSection {
  flex: 1;
}

.pageTitle {
  margin: 0;
  font-size: 24px;
  font-weight: 600;
  color: var(--color-neutral-900);
}

.pageDescription {
  margin: 4px 0 0;
  font-size: 14px;
  color: var(--color-neutral-500);
}

.pageActions {
  display: flex;
  gap: 12px;
  flex-shrink: 0;
}

.content {
  flex: 1;
  padding: 24px;
  overflow-y: auto;
}

@media (max-width: 768px) {
  .pageHeader {
    flex-direction: column;
    align-items: stretch;
  }

  .pageActions {
    margin-top: 16px;
  }

  .content {
    padding: 16px;
  }
}

AuthLayout 模板示例

// templates/AuthLayout/AuthLayout.tsx
import React from 'react';
import styles from './AuthLayout.module.css';

export interface AuthLayoutProps {
  /** 徽标元素 */
  logo: React.ReactNode;
  /** 页面标题 */
  title: string;
  /** 页面副标题 */
  subtitle?: string;
  /** 表单内容 */
  children: React.ReactNode;
  /** 底部内容(链接等) */
  footer?: React.ReactNode;
  /** 背景图片URL */
  backgroundImage?: string;
  /** 显示装饰性侧面板 */
  showSidePanel?: boolean;
  /** 侧面板内容 */
  sidePanelContent?: React.ReactNode;
}

export const AuthLayout: React.FC<AuthLayoutProps> = ({
  logo,
  title,
  subtitle,
  children,
  footer,
  backgroundImage,
  showSidePanel = false,
  sidePanelContent,
}) => {
  return (
    <div className={styles.layout}>
      {/* 侧面板(可选) */}
      {showSidePanel && (
        <div
          className={styles.sidePanel}
          style={
            backgroundImage
              ? { backgroundImage: `url(${backgroundImage})` }
              : undefined
          }
        >
          <div className={styles.sidePanelContent}>{sidePanelContent}</div>
        </div>
      )}

      {/* 主要内容 */}
      <div className={styles.main}>
        <div className={styles.container}>
          {/* 徽标 */}
          <div className={styles.logo}>{logo}</div>

          {/* 头部 */}
          <header className={styles.header}>
            <h1 className={styles.title}>{title}</h1>
            {subtitle && <p className={styles.subtitle}>{subtitle}</p>}
          </header>

          {/* 表单内容 */}
          <div className={styles.content}>{children}</div>

          {/* 底部 */}
          {footer && <footer className={styles.footer}>{footer}</footer>}
        </div>
      </div>
    </div>
  );
};

AuthLayout.displayName = 'AuthLayout';
/* templates/AuthLayout/AuthLayout.module.css */
.layout {
  display: flex;
  min-height: 100vh;
}

.sidePanel {
  display: none;
  width: 50%;
  background-color: var(--color-primary-600);
  background-size: cover;
  background-position: center;
  position: relative;
}

@media (min-width: 1024px) {
  .sidePanel {
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

.sidePanelContent {
  padding: 48px;
  color: var(--color-white);
  text-align: center;
}

.main {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background-color: var(--color-neutral-50);
}

.container {
  width: 100%;
  max-width: 400px;
}

.logo {
  text-align: center;
  margin-bottom: 32px;
}

.header {
  text-align: center;
  margin-bottom: 32px;
}

.title {
  margin: 0;
  font-size: 28px;
  font-weight: 700;
  color: var(--color-neutral-900);
}

.subtitle {
  margin: 8px 0 0;
  font-size: 16px;
  color: var(--color-neutral-500);
}

.content {
  background-color: var(--color-white);
  padding: 32px;
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}

.footer {
  margin-top: 24px;
  text-align: center;
  font-size: 14px;
  color: var(--color-neutral-500);
}

.footer a {
  color: var(--color-primary-500);
  text-decoration: none;
}

.footer a:hover {
  text-decoration: underline;
}

ProductListingLayout 模板示例

// templates/ProductListingLayout/ProductListingLayout.tsx
import React from 'react';
import { MainLayout, type MainLayoutProps } from '../MainLayout';
import styles from './ProductListingLayout.module.css';

export interface ProductListingLayoutProps {
  /** 主要布局属性 */
  layoutProps: Omit<MainLayoutProps, 'children'>;
  /** 类别标题 */
  categoryTitle: string;
  /** 类别描述 */
  categoryDescription?: string;
  /** 产品数量 */
  productCount: number;
  /** 过滤侧边栏内容 */
  filters: React.ReactNode;
  /** 排序/视图控制 */
  controls: React.ReactNode;
  /** 产品网格内容 */
  products: React.ReactNode;
  /** 分页内容 */
  pagination?: React.ReactNode;
  /** 在移动设备上显示过滤器 */
  mobileFiltersOpen?: boolean;
  /** 切换移动过滤器 */
  onToggleMobileFilters?: () => void;
}

export const ProductListingLayout: React.FC<ProductListingLayoutProps> = ({
  layoutProps,
  categoryTitle,
  categoryDescription,
  productCount,
  filters,
  controls,
  products,
  pagination,
  mobileFiltersOpen = false,
  onToggleMobileFilters,
}) => {
  return (
    <MainLayout {...layoutProps} maxWidth="xl">
      {/* 类别头部 */}
      <header className={styles.header}>
        <div className={styles.titleSection}>
          <h1 className={styles.title}>{categoryTitle}</h1>
          {categoryDescription && (
            <p className={styles.description}>{categoryDescription}</p>
          )}
          <span className={styles.count}>{productCount} 产品</span>
        </div>
      </header>

      <div className={styles.body}>
        {/* 桌面过滤器 */}
        <aside className={styles.sidebar}>
          <div className={styles.sidebarContent}>{filters}</div>
        </aside>

        {/* 移动过滤器覆盖层 */}
        {mobileFiltersOpen && (
          <div className={styles.mobileFilters}>
            <div className={styles.mobileFiltersHeader}>
              <h2>过滤器</h2>
              <button onClick={onToggleMobileFilters}>关闭</button>
            </div>
            <div className={styles.mobileFiltersContent}>{filters}</div>
          </div>
        )}

        {/* 主要内容 */}
        <div className={styles.main}>
          {/* 控制栏 */}
          <div className={styles.controls}>
            <button
              className={styles.mobileFilterButton}
              onClick={onToggleMobileFilters}
            >
              过滤器
            </button>
            {controls}
          </div>

          {/* 产品网格 */}
          <div className={styles.products}>{products}</div>

          {/* 分页 */}
          {pagination && (
            <div className={styles.pagination}>{pagination}</div>
          )}
        </div>
      </div>
    </MainLayout>
  );
};

ProductListingLayout.displayName = 'ProductListingLayout';

BlogPostLayout 模板示例

// templates/BlogPostLayout/BlogPostLayout.tsx
import React from 'react';
import { MainLayout, type MainLayoutProps } from '../MainLayout';
import { Avatar } from '@/components/atoms/Avatar';
import { Text } from '@/components/atoms/Typography';
import styles from './BlogPostLayout.module.css';

export interface Author {
  name: string;
  avatar?: string;
  bio?: string;
}

export interface BlogPostLayoutProps {
  /** 主要布局属性 */
  layoutProps: Omit<MainLayoutProps, 'children'>;
  /** 文章标题 */
  title: string;
  /** 文章副标题 */
  subtitle?: string;
  /** 特色图片 */
  featuredImage?: string;
  /** 作者信息 */
  author: Author;
  /** 发布日期 */
  publishedAt: string;
  /** 阅读时间 */
  readingTime?: string;
  /** 文章类别/标签 */
  tags?: React.ReactNode;
  /** 主要文章内容 */
  children: React.ReactNode;
  /** 目录 */
  tableOfContents?: React.ReactNode;
  /** 作者简介卡片 */
  showAuthorBio?: boolean;
  /** 相关文章 */
  relatedPosts?: React.ReactNode;
  /** 评论部分 */
  comments?: React.ReactNode;
  /** 社交分享按钮 */
  shareButtons?: React.ReactNode;
}

export const BlogPostLayout: React.FC<BlogPostLayoutProps> = ({
  layoutProps,
  title,
  subtitle,
  featuredImage,
  author,
  publishedAt,
  readingTime,
  tags,
  children,
  tableOfContents,
  showAuthorBio = true,
  relatedPosts,
  comments,
  shareButtons,
}) => {
  const formattedDate = new Date(publishedAt).toLocaleDateString('en-US', {
    month: 'long',
    day: 'numeric',
    year: 'numeric',
  });

  return (
    <MainLayout {...layoutProps} maxWidth="md">
      <article className={styles.article}>
        {/* 文章头部 */}
        <header className={styles.header}>
          {tags && <div className={styles.tags}>{tags}</div>}

          <h1 className={styles.title}>{title}</h1>

          {subtitle && <p className={styles.subtitle}>{subtitle}</p>}

          {/* 作者和元数据 */}
          <div className={styles.meta}>
            <div className={styles.author}>
              <Avatar
                src={author.avatar}
                alt={author.name}
                initials={author.name.slice(0, 2).toUpperCase()}
                size="md"
              />
              <div className={styles.authorInfo}>
                <Text weight="semibold">{author.name}</Text>
                <Text size="sm" color="muted">
                  {formattedDate}
                  {readingTime && ` · ${readingTime}`}
                </Text>
              </div>
            </div>

            {shareButtons && (
              <div className={styles.share}>{shareButtons}</div>
            )}
          </div>
        </header>

        {/* 特色图片 */}
        {featuredImage && (
          <figure className={styles.featuredImage}>
            <img src={featuredImage} alt={title} />
          </figure>
        )}

        {/* 内容与可选目录 */}
        <div className={styles.contentWrapper}>
          {/* 目录(桌面) */}
          {tableOfContents && (
            <aside className={styles.toc}>
              <div className={styles.tocContent}>{tableOfContents}</div>
            </aside>
          )}

          {/* 主要内容 */}
          <div className={styles.content}>{children}</div>
        </div>

        {/* 作者简介 */}
        {showAuthorBio && (
          <footer className={styles.authorBio}>
            <Avatar
              src={author.avatar}
              alt={author.name}
              initials={author.name.slice(0, 2).toUpperCase()}
              size="lg"
            />
            <div>
              <Text weight="semibold" size="lg">
                {author.name}
              </Text>
              {author.bio && <Text color="muted">{author.bio}</Text>}
            </div>
          </footer>
        )}

        {/* 分享(底部) */}
        {shareButtons && (
          <div className={styles.bottomShare}>{shareButtons}</div>
        )}
      </article>

      {/* 相关文章 */}
      {relatedPosts && (
        <section className={styles.relatedPosts}>
          <h2>相关文章</h2>
          {relatedPosts}
        </section>
      )}

      {/* 评论 */}
      {comments && (
        <section className={styles.comments}>{comments}</section>
      )}
    </MainLayout>
  );
};

BlogPostLayout.displayName = 'BlogPostLayout';

TwoColumnLayout 模板示例

// templates/TwoColumnLayout/TwoColumnLayout.tsx
import React from 'react';
import styles from './TwoColumnLayout.module.css';

export interface TwoColumnLayoutProps {
  /** 左列(通常为主要内容) */
  main: React.ReactNode;
  /** 右列(通常为侧边栏) */
  sidebar: React.ReactNode;
  /** 侧边栏位置 */
  sidebarPosition?: 'left' | 'right';
  /** 侧边栏宽度 */
  sidebarWidth?: 'narrow' | 'medium' | 'wide';
  /** 粘性侧边栏 */
  stickySidebar?: boolean;
  /** 在移动设备上反转(先显示侧边栏) */
  reverseMobile?: boolean;
  /** 列之间的间隙 */
  gap?: 'sm' | 'md' | 'lg';
}

export const TwoColumnLayout: React.FC<TwoColumnLayoutProps> = ({
  main,
  sidebar,
  sidebarPosition = 'right',
  sidebarWidth = 'medium',
  stickySidebar = false,
  reverseMobile = false,
  gap = 'md',
}) => {
  const layoutClass = [
    styles.layout,
    styles[`sidebar-${sidebarPosition}`],
    styles[`width-${sidebarWidth}`],
    styles[`gap-${gap}`],
    reverseMobile && styles.reverseMobile,
  ]
    .filter(Boolean)
    .join(' ');

  const sidebarClass = [
    styles.sidebar,
    stickySidebar && styles.sticky,
  ]
    .filter(Boolean)
    .join(' ');

  return (
    <div className={layoutClass}>
      <main className={styles.main}>{main}</main>
      <aside className={sidebarClass}>{sidebar}</aside>
    </div>
  );
};

TwoColumnLayout.displayName = 'TwoColumnLayout';

最佳实践

1. 使用占位内容

// 好:模板带有占位内容槽
const ProductDetailLayout = ({
  productGallery,    // 画廊组件的占位符
  productInfo,       // 产品详情的占位符
  productTabs,       // 标签的占位符
  relatedProducts,   // 推荐产品的占位符
}) => (
  <div>
    <section>{productGallery}</section>
    <section>{productInfo}</section>
    <section>{productTabs}</section>
    <section>{relatedProducts}</section>
  </div>
);

// 坏:模板带有硬编码内容
const ProductDetailLayout = ({ product }) => (
  <div>
    <ProductGallery images={product.images} />      {/* 太具体 */}
    <h1>{product.name}</h1>                          {/* 真实内容 */}
    <p>{product.description}</p>
  </div>
);

2. 定义清晰的内容区域

// 好:清晰、命名的内容槽
interface PageTemplateProps {
  header: React.ReactNode;
  hero?: React.ReactNode;
  main: React.ReactNode;
  sidebar?: React.ReactNode;
  footer: React.ReactNode;
}

// 坏:仅通用子元素
interface PageTemplateProps {
  children: React.ReactNode;
}

3. 处理响应式布局

// 好:内置响应式考虑
const DashboardLayout = ({ sidebar, main }) => (
  <div className={styles.layout}>
    <aside className={styles.sidebar}>{sidebar}</aside>
    <main className={styles.main}>{main}</main>
  </div>
);

// CSS 处理响应式行为
// .sidebar { @media (max-width: 768px) { display: none; } }

4. 保持模板简洁

// 好:模板仅排列生物体
const MainLayout = ({ header, main, footer }) => (
  <div className={styles.layout}>
    <div className={styles.header}>{header}</div>
    <div className={styles.main}>{main}</div>
    <div className={styles.footer}>{footer}</div>
  </div>
);

// 坏:模板带有业务逻辑
const MainLayout = ({ userId }) => {
  const user = useUser(userId);          // 获取数据
  const isAdmin = user?.role === 'admin'; // 业务逻辑

  return (
    <div>
      <Header user={user} showAdmin={isAdmin} />
      {/* ... */}
    </div>
  );
};

要避免的反模式

1. 模板带有真实内容

// 坏:硬编码真实内容
const HomepageLayout = () => (
  <div>
    <h1>欢迎来到我们的商店</h1>           {/* 真实内容! */}
    <p>购买我们的最新系列...</p>    {/* 真实内容! */}
  </div>
);

// 好:内容作为属性/子元素传递
const HomepageLayout = ({ heroTitle, heroDescription }) => (
  <div>
    <h1>{heroTitle}</h1>
    <p>{heroDescription}</p>
  </div>
);

2. 过度嵌套的模板

// 坏:模板包含模板
const AppLayout = () => (
  <BaseLayout>
    <AuthLayout>
      <DashboardLayout>
        {/* 嵌套过多 */}
      </DashboardLayout>
    </AuthLayout>
  </BaseLayout>
);

// 好:直接选择适当的模板
const AppPage = () => (
  <DashboardLayout>
    {/* 内容 */}
  </DashboardLayout>
);

3. 模板属性过多

// 坏:太多配置选项
interface LayoutProps {
  showHeader: boolean;
  showFooter: boolean;
  showSidebar: boolean;
  sidebarPosition: 'left' | 'right';
  headerVariant: 'default' | 'minimal' | 'transparent';
  footerVariant: 'default' | 'minimal';
  maxWidth: 'sm' | 'md' | 'lg' | 'xl';
  // ... 20 更多属性
}

// 好:创建单独的模板
const FullPageLayout = ({ ... }) => { ... };
const MinimalLayout = ({ ... }) => { ... };
const SidebarLayout = ({ ... }) => { ... };

模板组合模式

嵌套布局

// 所有页面的基础布局
const BaseLayout = ({ children }) => (
  <div className="base">
    <SkipLink />
    {children}
  </div>
);

// 营销布局扩展基础
const MarketingLayout = ({ children }) => (
  <BaseLayout>
    <MarketingHeader />
    <main>{children}</main>
    <MarketingFooter />
  </BaseLayout>
);

// 应用布局扩展基础
const AppLayout = ({ children }) => (
  <BaseLayout>
    <AppHeader />
    <main>{children}</main>
  </BaseLayout>
);

基于槽的布局

interface SlotLayoutProps {
  slots: {
    header?: React.ReactNode;
    sidebar?: React.ReactNode;
    main: React.ReactNode;
    footer?: React.ReactNode;
  };
}

const SlotLayout: React.FC<SlotLayoutProps> = ({ slots }) => (
  <div className={styles.layout}>
    {slots.header && <header>{slots.header}</header>}
    <div className={styles.body}>
      {slots.sidebar && <aside>{slots.sidebar}</aside>}
      <main>{slots.main}</main>
    </div>
    {slots.footer && <footer>{slots.footer}</footer>}
  </div>
);

何时使用此技能

  • 创建页面结构模式
  • 构建可重用的布局组件
  • 建立一致的页面架构
  • 设置响应式框架
  • 定义内容槽模式

相关技能

  • atomic-design-fundamentals - 核心方法论概述
  • atomic-design-organisms - 构建复杂生物体
  • atomic-design-integration - 框架特定模式