基础

布局

用于快捷划分页面整体布局

概述

  • Layout:布局容器,其下可嵌套 Header Sider Content Footer 或 Layout 本身,可以放在任何父容器中。
  • Header:顶部布局,其下可嵌套任何元素,只能放在 Layout 中。
  • Sider:侧边栏,其下可嵌套任何元素,只能放在 Layout 中。
  • Content:内容部分,其下可嵌套任何元素,只能放在 Layout 中。
  • Footer:底部布局,其下可嵌套任何元素,只能放在 Layout 中。

代码演示

如何引入

import { Layout } from '@fibrous/semi-ui-react';

三行布局

Header
Content
Footer
代码
import { Layout } from '@fibrous/semi-ui-react';

export default function LayoutDemo() {
    const { Header, Footer, Content } = Layout;

    const commonStyle = {
        height: 64,
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };

    return (
        <Layout className="components-layout-demo">
            <Header style={commonStyle}>Header</Header>
            <Content style={{ height: 300, lineHeight: '300px' }}>Content</Content>
            <Footer style={commonStyle}>Footer</Footer>
        </Layout>
    );
}

左侧边栏布局

Header
Content
Footer
代码
import { Layout } from '@fibrous/semi-ui-react';

export default function LayoutDemo() {
    const { Header, Footer, Sider, Content } = Layout;
    const commonStyle = {
        height: 64,
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
    return (
        <Layout className="components-layout-demo">
            <Header style={commonStyle}>Header</Header>
            <Layout>
                <Sider style={{ width: '120px', background: 'var(--semi-color-fill-2)' }}>Sider</Sider>
                <Content style={{ height: 300, lineHeight: '300px' }}>Content</Content>
            </Layout>
            <Footer style={commonStyle}>Footer</Footer>
        </Layout>
    );
}

右侧边栏布局

Header
Content
Footer
代码
import { Layout } from '@fibrous/semi-ui-react';

export default function LayoutDemo() {
    const { Header, Footer, Sider, Content } = Layout;
    const commonStyle = {
        height: 64,
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
    return (
        <Layout className="components-layout-demo">
            <Header style={commonStyle}>Header</Header>
            <Layout>
                <Content style={{ height: 300, lineHeight: '300px' }}>Content</Content>
                <Sider style={{ width: '120px', background: 'var(--semi-color-fill-2)' }}>Sider</Sider>
            </Layout>
            <Footer style={commonStyle}>Footer</Footer>
        </Layout>
    );
}

侧边栏布局

Header
Content
Footer
代码
import { Layout } from '@fibrous/semi-ui-react';

export default function LayoutDemo() {
    const { Header, Footer, Sider, Content } = Layout;
    const commonStyle = {
        height: 64,
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
    return (
        <Layout className="components-layout-demo">
            <Sider style={{ width: '120px', background: 'var(--semi-color-fill-2)' }}>Sider</Sider>
            <Layout>
                <Header style={commonStyle}>Header</Header>
                <Content style={{ height: 300, lineHeight: '300px' }}>Content</Content>
                <Footer style={commonStyle}>Footer</Footer>
            </Layout>
        </Layout>
    );
}

响应式布局

侧边栏预设了六个响应尺寸:xs、sm、md、lg、xl、xxl。可以通过设置 breakpoint 属性设置断点。

通过 onBreakpoint 调用回调函数。

Header
Content
Footer
代码
import { Layout, type SiderProps } from '@fibrous/semi-ui-react';

export default function LayoutDemo() {
    const onbreakpoint: SiderProps['onBreakpoint'] = (screen, bool) => {
        console.log(screen, bool);
    };
    const commonStyle = {
        height: 64,
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
    const { Header, Footer, Sider, Content } = Layout;
    return (
        <Layout className="components-layout-demo">
            <Header style={commonStyle}>Header</Header>
            <Layout>
                <Sider style={{ width: '120px', background: 'var(--semi-color-fill-2)' }} breakpoint={['md']} onBreakpoint={onbreakpoint}>
                    Sider
                </Sider>
                <Content style={{ height: 300, lineHeight: '300px' }}>Content</Content>
            </Layout>
            <Footer style={commonStyle}>Footer</Footer>
        </Layout>
    );
}

布局示例

顶部导航布局

Copyright © 2023 ByteDance. All Rights Reserved. 平台客服反馈建议
代码
import { IconSemiLogo, IconBell, IconHelpCircle, IconBytedanceLogo, IconHome, IconLive, IconSetting } from '@fibrous/semi-icons-react';
import { Layout, Nav, Button, Breadcrumb, Skeleton, Avatar } from '@fibrous/semi-ui-react';

export default function LayoutDemo() {
    const { Header, Footer, Content } = Layout;
    return (
        <Layout style={{ border: '1px solid var(--semi-color-border)' }}>
            <Header style={{ backgroundColor: 'var(--semi-color-bg-1)' }}>
                <div>
                    <Nav mode="horizontal" defaultSelectedKeys={['Home']}>
                        <Nav.Header>
                            <IconSemiLogo style={{ fontSize: 36 }} />
                        </Nav.Header>
                        <Nav.Item itemKey="Home" text="首页" icon={<IconHome size="large" />} />
                        <Nav.Item itemKey="Live" text="直播" icon={<IconLive size="large" />} />
                        <Nav.Item itemKey="Setting" text="设置" icon={<IconSetting size="large" />} />
                        <Nav.Footer>
                            <Button
                                theme="borderless"
                                aria-label="通知"
                                icon={<IconBell size="large" />}
                                style={{
                                    color: 'var(--semi-color-text-2)',
                                    marginRight: '12px',
                                }}
                            />
                            <Button
                                theme="borderless"
                                aria-label="帮助"
                                icon={<IconHelpCircle size="large" />}
                                style={{
                                    color: 'var(--semi-color-text-2)',
                                    marginRight: '12px',
                                }}
                            />
                            <Avatar color="orange" size="small">
                                YJ
                            </Avatar>
                        </Nav.Footer>
                    </Nav>
                </div>
            </Header>
            <Content
                style={{
                    padding: '24px',
                    backgroundColor: 'var(--semi-color-bg-0)',
                }}
            >
                <Breadcrumb
                    style={{
                        marginBottom: '24px',
                    }}
                    routes={['首页', '当这个页面标题很长时需要省略', '上一页', '详情页']}
                />
                <div
                    style={{
                        borderRadius: '10px',
                        border: '1px solid var(--semi-color-border)',
                        height: '376px',
                        padding: '32px',
                    }}
                >
                    <Skeleton placeholder={<Skeleton.Paragraph rows={2} />} loading>
                        <p>Hi, Bytedance dance dance.</p>
                        <p>Hi, Bytedance dance dance.</p>
                    </Skeleton>
                </div>
            </Content>
            <Footer
                style={{
                    display: 'flex',
                    justifyContent: 'space-between',
                    padding: '20px',
                    color: 'var(--semi-color-text-2)',
                    backgroundColor: 'var(--semi-grey-0)',
                }}
            >
                <span
                    style={{
                        display: 'flex',
                        alignItems: 'center',
                    }}
                >
                    <IconBytedanceLogo size="large" style={{ marginRight: '8px' }} />
                    <span>Copyright © 2023 ByteDance. All Rights Reserved. </span>
                </span>
                <span>
                    <span style={{ marginRight: '24px' }}>平台客服</span>
                    <span>反馈建议</span>
                </span>
            </Footer>
        </Layout>
    );
}

顶部导航-侧边布局

Copyright © 2023 ByteDance. All Rights Reserved. 平台客服反馈建议
代码
import { IconSemiLogo, IconBell, IconHelpCircle, IconBytedanceLogo, IconHome, IconHistogram, IconLive, IconSetting } from '@fibrous/semi-icons-react';
import { Layout, Nav, Button, Breadcrumb, Skeleton, Avatar } from '@fibrous/semi-ui-react';

export default function LayoutDemo() {
    const { Header, Footer, Sider, Content } = Layout;
    return (
        <Layout style={{ border: '1px solid var(--semi-color-border)' }}>
            <Header style={{ backgroundColor: 'var(--semi-color-bg-1)' }}>
                <div>
                    <Nav aria-label="顶部导航" mode="horizontal" defaultSelectedKeys={['Home']}>
                        <Nav.Header>
                            <IconSemiLogo style={{ height: '36px', fontSize: 36 }} />
                        </Nav.Header>
                        <span
                            style={{
                                color: 'var(--semi-color-text-2)',
                            }}
                        >
                            <span
                                style={{
                                    marginRight: '24px',
                                    color: 'var(--semi-color-text-0)',
                                    fontWeight: '600',
                                }}
                            >
                                模版推荐
                            </span>
                            <span style={{ marginRight: '24px' }}>所有模版</span>
                            <span>我的模版</span>
                        </span>
                        <Nav.Footer>
                            <Button
                                theme="borderless"
                                aria-label="通知"
                                icon={<IconBell size="large" />}
                                style={{
                                    color: 'var(--semi-color-text-2)',
                                    marginRight: '12px',
                                }}
                            />
                            <Button
                                theme="borderless"
                                aria-label="帮助"
                                icon={<IconHelpCircle size="large" />}
                                style={{
                                    color: 'var(--semi-color-text-2)',
                                    marginRight: '12px',
                                }}
                            />
                            <Avatar color="orange" size="small">
                                YJ
                            </Avatar>
                        </Nav.Footer>
                    </Nav>
                </div>
            </Header>
            <Layout>
                <Sider style={{ backgroundColor: 'var(--semi-color-bg-1)' }}>
                    <Nav
                        aria-label="侧边导航"
                        style={{ maxWidth: 220, height: '100%' }}
                        defaultSelectedKeys={['Home']}
                        items={[
                            { itemKey: 'Home', text: '首页', icon: <IconHome size="large" /> },
                            { itemKey: 'Histogram', text: '基础数据', icon: <IconHistogram size="large" /> },
                            { itemKey: 'Live', text: '测试功能', icon: <IconLive size="large" /> },
                            { itemKey: 'Setting', text: '设置', icon: <IconSetting size="large" /> },
                        ]}
                        footer={{
                            collapseButton: true,
                        }}
                    />
                </Sider>
                <Content
                    style={{
                        padding: '24px',
                        backgroundColor: 'var(--semi-color-bg-0)',
                    }}
                >
                    <Breadcrumb
                        style={{
                            marginBottom: '24px',
                        }}
                        routes={['首页', '当这个页面标题很长时需要省略', '上一页', '详情页']}
                    />
                    <div
                        style={{
                            borderRadius: '10px',
                            border: '1px solid var(--semi-color-border)',
                            height: '376px',
                            padding: '32px',
                        }}
                    >
                        <Skeleton placeholder={<Skeleton.Paragraph rows={2} />} loading>
                            <p>Hi, Bytedance dance dance.</p>
                            <p>Hi, Bytedance dance dance.</p>
                        </Skeleton>
                    </div>
                </Content>
            </Layout>
            <Footer
                style={{
                    display: 'flex',
                    justifyContent: 'space-between',
                    padding: '20px',
                    color: 'var(--semi-color-text-2)',
                    backgroundColor: 'var(--semi-grey-0)',
                }}
            >
                <span
                    style={{
                        display: 'flex',
                        alignItems: 'center',
                    }}
                >
                    <IconBytedanceLogo size="large" style={{ marginRight: '8px' }} />
                    <span>Copyright © 2023 ByteDance. All Rights Reserved. </span>
                </span>
                <span>
                    <span style={{ marginRight: '24px' }}>平台客服</span>
                    <span>反馈建议</span>
                </span>
            </Footer>
        </Layout>
    );
}

侧边导航

Copyright © 2019 ByteDance. All Rights Reserved. 平台客服反馈建议
代码
import { IconBell, IconHelpCircle, IconBytedanceLogo, IconHome, IconHistogram, IconLive, IconSetting, IconSemiLogo } from '@fibrous/semi-icons-react';
import { Layout, Nav, Button, Breadcrumb, Skeleton, Avatar } from '@fibrous/semi-ui-react';

export default function LayoutDemo() {
    const { Header, Footer, Sider, Content } = Layout;
    return (
        <Layout style={{ border: '1px solid var(--semi-color-border)' }}>
            <Sider style={{ backgroundColor: 'var(--semi-color-bg-1)' }}>
                <Nav
                    aria-label="侧边导航"
                    defaultSelectedKeys={['Home']}
                    style={{ maxWidth: 220, height: '100%' }}
                    items={[
                        { itemKey: 'Home', text: '首页', icon: <IconHome size="large" /> },
                        { itemKey: 'Histogram', text: '基础数据', icon: <IconHistogram size="large" /> },
                        { itemKey: 'Live', text: '测试功能', icon: <IconLive size="large" /> },
                        { itemKey: 'Setting', text: '设置', icon: <IconSetting size="large" /> },
                    ]}
                    header={{
                        logo: <IconSemiLogo style={{ fontSize: 36 }} />,
                        text: 'Semi Design',
                    }}
                    footer={{
                        collapseButton: true,
                    }}
                />
            </Sider>
            <Layout>
                <Header style={{ backgroundColor: 'var(--semi-color-bg-1)' }}>
                    <Nav
                        aria-label="顶部导航"
                        mode="horizontal"
                        footer={
                            <>
                                <Button
                                    theme="borderless"
                                    aria-label="通知"
                                    icon={<IconBell size="large" />}
                                    style={{
                                        color: 'var(--semi-color-text-2)',
                                        marginRight: '12px',
                                    }}
                                />
                                <Button
                                    theme="borderless"
                                    aria-label="帮助"
                                    icon={<IconHelpCircle size="large" />}
                                    style={{
                                        color: 'var(--semi-color-text-2)',
                                        marginRight: '12px',
                                    }}
                                />
                                <Avatar color="orange" size="small">
                                    YJ
                                </Avatar>
                            </>
                        }
                    />
                </Header>
                <Content
                    style={{
                        padding: '24px',
                        backgroundColor: 'var(--semi-color-bg-0)',
                    }}
                >
                    <Breadcrumb
                        style={{
                            marginBottom: '24px',
                        }}
                        routes={['首页', '当这个页面标题很长时需要省略', '上一页', '详情页']}
                    />
                    <div
                        style={{
                            borderRadius: '10px',
                            border: '1px solid var(--semi-color-border)',
                            height: '376px',
                            padding: '32px',
                        }}
                    >
                        <Skeleton placeholder={<Skeleton.Paragraph rows={2} />} loading>
                            <p>Hi, Bytedance dance dance.</p>
                            <p>Hi, Bytedance dance dance.</p>
                        </Skeleton>
                    </div>
                </Content>
                <Footer
                    style={{
                        display: 'flex',
                        justifyContent: 'space-between',
                        padding: '20px',
                        color: 'var(--semi-color-text-2)',
                        backgroundColor: 'var(--semi-grey-0)',
                    }}
                >
                    <span
                        style={{
                            display: 'flex',
                            alignItems: 'center',
                        }}
                    >
                        <IconBytedanceLogo size="large" style={{ marginRight: '8px' }} />
                        <span>Copyright © 2019 ByteDance. All Rights Reserved. </span>
                    </span>
                    <span>
                        <span style={{ marginRight: '24px' }}>平台客服</span>
                        <span>反馈建议</span>
                    </span>
                </Footer>
            </Layout>
        </Layout>
    );
}

API 参考

Layout

Props

属性说明类型默认值
children布局内容,可包含 Layout.Header、Layout.Sider、Layout.Content、Layout.Footer 或嵌套的 Layout。React.ReactNode-
className根元素的类名。string-
hasSider表示子元素里有 Sider,一般不用指定。直接子元素中的 Sider 在渲染时即可识别;更深层嵌套的 Sider 在挂载后才登记,服务端渲染时传 true 可避免样式闪动。传 false 不能去掉 Sider 带来的 has-sider 类。boolean-
prefixCls类名前缀。string"semi-layout"
ref根元素的引用,元素的标签由 tagName 决定。TypeReact.Ref<HTMLElement>-
style根元素的样式。CSSProperties-
tagName根元素的标签名。keyof HTMLElementTagNameMap"section"

未列出的原生属性(含 role、aria-*)落在根元素上。

Layout.Header Layout.Footer Layout.Content API 与 Layout 相同,但没有 hasSider,另有内部使用的 type

属性说明类型默认值
className根元素的类名。string-
prefixCls类名前缀。stringsemi-layout
style根元素的样式。CSSProperties-
tagName根元素的标签名。keyof HTMLElementTagNameMapheader、footer、main 分别为 Header、Footer、Content 的默认值

ref 与未列出的原生属性(含 role、aria-*)落在各自的根元素上。

Layout.Sider

Props

属性说明类型默认值
aria-label侧边栏的可访问名称,描述该 Sider 的作用。React.AriaAttributes['aria-label']-
breakpoint触发响应式布局的断点,可选 xs、sm、md、lg、xl 或 xxl。仅在挂载时读取一次。TypeArray<keyof ResponsiveMap>-
children侧边栏内容,渲染在根元素内的包裹 div 中。React.ReactNode-
className根元素的类名。string-
onBreakpoint断点命中状态变化时调用,参数为断点名与是否命中。挂载时对 breakpoint 中的每个断点按当前视口调用一次,之后在命中或离开时调用。Type(screen: keyof ResponsiveMap, match: boolean) => void-
prefixCls类名前缀。string"semi-layout"
ref根元素 aside 的引用。TypeReact.Ref<HTMLElement>-
role根元素 aside 的 role。React.AriaRole-
style根元素的样式。CSSProperties-

未列出的原生属性(含 aria-*、onClick)落在根元素 aside 上。

responsive map

{
    xs: '(max-width: 575px)',
    sm: '(min-width: 576px)',
    md: '(min-width: 768px)',
    lg: '(min-width: 992px)',
    xl: '(min-width: 1200px)',
    xxl: '(min-width: 1600px)',
};

Accessibility

ARIA

  • Sider 渲染 aside,可传入 aria-label 描述该 Sider 的作用。
  • Header、Footer 默认渲染 header、footer,位于 Layout 的 section 内时不会成为 banner、contentinfo 地标;作为整页的页头、页脚时传入 role="banner"、role="contentinfo"。
  • Content 默认渲染 main,一个页面只应有一个 main;嵌套使用时可通过 tagName 换成其他标签。
  • Header、Content、Footer 可传入 role、aria-label 描述对应元素的作用。