Basic · Layout

Layout

Used to quickly divide the overall layout of the page

Overview

  • Layout: Layout containers. You can nest Header Sider Content Footer or Layout itself inside.
  • Header: Head component, can only be used inside Layout.
  • Sider: Sidebar, can only be used inside Layout.
  • Content: Content component, can only be used inside Layout.
  • Footer: Footer component, can only be used inside Layout.

Demos

How to import

<script lang="ts">
    import { Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider } from '@fibrae/semi-ui-svelte';
</script>

The subcomponents are LayoutHeader, LayoutSider, LayoutContent and LayoutFooter, which stand for Layout.Header, Layout.Sider, Layout.Content and Layout.Footer below, and are imported by name.

Three-section Layout

Header
Content
Footer
Code
<script lang="ts">
    import { Layout, LayoutContent, LayoutFooter, LayoutHeader } from '@fibrae/semi-ui-svelte';

    const commonStyle = 'height: 64px; line-height: 64px; background: var(--semi-color-fill-0)';
</script>

<Layout class="components-layout-demo">
    <LayoutHeader style={commonStyle}>Header</LayoutHeader>
    <LayoutContent style="height: 300px; line-height: 300px">Content</LayoutContent>
    <LayoutFooter style={commonStyle}>Footer</LayoutFooter>
</Layout>
Header
Content
Footer
Code
<script lang="ts">
    import { Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider } from '@fibrae/semi-ui-svelte';

    const commonStyle = 'height: 64px; line-height: 64px; background: var(--semi-color-fill-0)';
</script>

<Layout class="components-layout-demo">
    <LayoutHeader style={commonStyle}>Header</LayoutHeader>
    <Layout>
        <LayoutSider style="width: 120px; background: var(--semi-color-fill-2)">Sider</LayoutSider>
        <LayoutContent style="height: 300px; line-height: 300px">Content</LayoutContent>
    </Layout>
    <LayoutFooter style={commonStyle}>Footer</LayoutFooter>
</Layout>
Header
Content
Footer
Code
<script lang="ts">
    import { Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider } from '@fibrae/semi-ui-svelte';

    const commonStyle = 'height: 64px; line-height: 64px; background: var(--semi-color-fill-0)';
</script>

<Layout class="components-layout-demo">
    <LayoutHeader style={commonStyle}>Header</LayoutHeader>
    <Layout>
        <LayoutContent style="height: 300px; line-height: 300px">Content</LayoutContent>
        <LayoutSider style="width: 120px; background: var(--semi-color-fill-2)">Sider</LayoutSider>
    </Layout>
    <LayoutFooter style={commonStyle}>Footer</LayoutFooter>
</Layout>
Header
Content
Footer
Code
<script lang="ts">
    import { Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider } from '@fibrae/semi-ui-svelte';

    const commonStyle = 'height: 64px; line-height: 64px; background: var(--semi-color-fill-0)';
</script>

<Layout class="components-layout-demo">
    <LayoutSider style="width: 120px; background: var(--semi-color-fill-2)">Sider</LayoutSider>
    <Layout>
        <LayoutHeader style={commonStyle}>Header</LayoutHeader>
        <LayoutContent style="height: 300px; line-height: 300px">Content</LayoutContent>
        <LayoutFooter style={commonStyle}>Footer</LayoutFooter>
    </Layout>
</Layout>

Responsive Layout

Six response sizes are preset in the sidebar: xs,sm,md,lg,xl,xxl. You can use breakpoint to set breakpoints.

Use onBreakpoint to call callback functions.

Header
Content
Footer
Code
<script lang="ts">
    import { Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider } from '@fibrae/semi-ui-svelte';

    const commonStyle = 'height: 64px; line-height: 64px; background: var(--semi-color-fill-0)';
</script>

<Layout class="components-layout-demo">
    <LayoutHeader style={commonStyle}>Header</LayoutHeader>
    <Layout>
        <LayoutSider breakpoint={['md']} onBreakpoint={(screen, matches) => console.log(screen, matches)} style="width: 120px; background: var(--semi-color-fill-2)">Sider</LayoutSider>
        <LayoutContent style="height: 300px; line-height: 300px">Content</LayoutContent>
    </Layout>
    <LayoutFooter style={commonStyle}>Footer</LayoutFooter>
</Layout>

Layout Examples

Top-nav Layout

Copyright © 2023 ByteDance. All Rights Reserved. 平台客服 反馈建议
Code
<script lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrae/semi-icons-svelte';
    import { Avatar, Breadcrumb, Button, Layout, LayoutContent, LayoutFooter, LayoutHeader, Nav, NavFooter, NavHeader, NavItem, Skeleton, SkeletonParagraph } from '@fibrae/semi-ui-svelte';

    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = 'color: var(--semi-color-text-2); margin-inline-end: 12px';
</script>

{#snippet header()}
    <NavHeader>
        <IconSemiLogo style="font-size: 36px" />
    </NavHeader>
{/snippet}

{#snippet footer()}
    <NavFooter>
        <Button aria-label="通知" style={actionStyle} theme="borderless">
            {#snippet icon()}
                <IconBell size="large" />
            {/snippet}
        </Button>
        <Button aria-label="帮助" style={actionStyle} theme="borderless">
            {#snippet icon()}
                <IconHelpCircle size="large" />
            {/snippet}
        </Button>
        <Avatar color="orange" size="small">YJ</Avatar>
    </NavFooter>
{/snippet}

<Layout style="border: 1px solid var(--semi-color-border)">
    <LayoutHeader style="background-color: var(--semi-color-bg-1)">
        <div>
            <Nav {footer} {header} mode="horizontal" selectedKeys={['Home']}>
                <NavItem itemKey="Home" text="首页">
                    {#snippet icon()}
                        <IconHome size="large" />
                    {/snippet}
                </NavItem>
                <NavItem itemKey="Live" text="直播">
                    {#snippet icon()}
                        <IconLive size="large" />
                    {/snippet}
                </NavItem>
                <NavItem itemKey="Setting" text="设置">
                    {#snippet icon()}
                        <IconSetting size="large" />
                    {/snippet}
                </NavItem>
            </Nav>
        </div>
    </LayoutHeader>
    <LayoutContent style="padding: 24px; background-color: var(--semi-color-bg-0)">
        <Breadcrumb {routes} style="margin-bottom: 24px" />
        <div style="border-radius: 10px; border: 1px solid var(--semi-color-border); height: 376px; padding: 32px">
            <Skeleton loading>
                {#snippet placeholder()}
                    <SkeletonParagraph rows={2} />
                {/snippet}
                <p>Hi, Bytedance dance dance.</p>
                <p>Hi, Bytedance dance dance.</p>
            </Skeleton>
        </div>
    </LayoutContent>
    <LayoutFooter style="display: flex; justify-content: space-between; padding: 20px; color: var(--semi-color-text-2); background-color: var(--semi-grey-0)">
        <span style="display: flex; align-items: center">
            <IconBytedanceLogo size="large" style="margin-inline-end: 8px" />
            <span>Copyright © 2023 ByteDance. All Rights Reserved. </span>
        </span>
        <span>
            <span style="margin-inline-end: 24px">平台客服</span>
            <span>反馈建议</span>
        </span>
    </LayoutFooter>
</Layout>

Top-Nav SideBar Layout

Copyright © 2023 ByteDance. All Rights Reserved. 平台客服 反馈建议
Code
<script lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHistogram, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrae/semi-icons-svelte';
    import { Avatar, Breadcrumb, Button, Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider, Nav, NavFooter, NavHeader, Skeleton, SkeletonParagraph, type NavItems } from '@fibrae/semi-ui-svelte';

    const items: NavItems = [
        { itemKey: 'Home', text: '首页' },
        { itemKey: 'Histogram', text: '基础数据' },
        { itemKey: 'Live', text: '测试功能' },
        { itemKey: 'Setting', text: '设置' },
    ];
    const icons = { Home: IconHome, Histogram: IconHistogram, Live: IconLive, Setting: IconSetting };
    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = 'color: var(--semi-color-text-2); margin-inline-end: 12px';
</script>

{#snippet topHeader()}
    <NavHeader>
        <IconSemiLogo style="height: 36px; font-size: 36px" />
    </NavHeader>
{/snippet}

{#snippet topFooter()}
    <NavFooter>
        <Button aria-label="通知" style={actionStyle} theme="borderless">
            {#snippet icon()}
                <IconBell size="large" />
            {/snippet}
        </Button>
        <Button aria-label="帮助" style={actionStyle} theme="borderless">
            {#snippet icon()}
                <IconHelpCircle size="large" />
            {/snippet}
        </Button>
        <Avatar color="orange" size="small">YJ</Avatar>
    </NavFooter>
{/snippet}

<Layout style="border: 1px solid var(--semi-color-border)">
    <LayoutHeader style="background-color: var(--semi-color-bg-1)">
        <div>
            <Nav aria-label="顶部导航" footer={topFooter} header={topHeader} mode="horizontal" selectedKeys={['Home']}>
                <!-- Nav renders its children straight into the list, so other content needs its own li -->
                <li style="display: contents">
                    <span style="color: var(--semi-color-text-2)">
                        <span style="margin-inline-end: 24px; color: var(--semi-color-text-0); font-weight: 600">模版推荐</span>
                        <span style="margin-inline-end: 24px">所有模版</span>
                        <span>我的模版</span>
                    </span>
                </li>
            </Nav>
        </div>
    </LayoutHeader>
    <Layout>
        <LayoutSider style="background-color: var(--semi-color-bg-1)">
            <Nav aria-label="侧边导航" footer={{ collapseButton: true }} {items} selectedKeys={['Home']} style="max-width: 220px; height: 100%">
                {#snippet icon({ item })}
                    {@const Icon = icons[item.itemKey as keyof typeof icons]}
                    <Icon size="large" />
                {/snippet}
            </Nav>
        </LayoutSider>
        <LayoutContent style="padding: 24px; background-color: var(--semi-color-bg-0)">
            <Breadcrumb {routes} style="margin-bottom: 24px" />
            <div style="border-radius: 10px; border: 1px solid var(--semi-color-border); height: 376px; padding: 32px">
                <Skeleton loading>
                    {#snippet placeholder()}
                        <SkeletonParagraph rows={2} />
                    {/snippet}
                    <p>Hi, Bytedance dance dance.</p>
                    <p>Hi, Bytedance dance dance.</p>
                </Skeleton>
            </div>
        </LayoutContent>
    </Layout>
    <LayoutFooter style="display: flex; justify-content: space-between; padding: 20px; color: var(--semi-color-text-2); background-color: var(--semi-grey-0)">
        <span style="display: flex; align-items: center">
            <IconBytedanceLogo size="large" style="margin-inline-end: 8px" />
            <span>Copyright © 2023 ByteDance. All Rights Reserved. </span>
        </span>
        <span>
            <span style="margin-inline-end: 24px">平台客服</span>
            <span>反馈建议</span>
        </span>
    </LayoutFooter>
</Layout>
Copyright © 2019 ByteDance. All Rights Reserved. 平台客服 反馈建议
Code
<script lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHistogram, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrae/semi-icons-svelte';
    import { Avatar, Breadcrumb, Button, Layout, LayoutContent, LayoutFooter, LayoutHeader, LayoutSider, Nav, NavFooter, NavHeader, Skeleton, SkeletonParagraph, type NavItems } from '@fibrae/semi-ui-svelte';

    const items: NavItems = [
        { itemKey: 'Home', text: '首页' },
        { itemKey: 'Histogram', text: '基础数据' },
        { itemKey: 'Live', text: '测试功能' },
        { itemKey: 'Setting', text: '设置' },
    ];
    const icons = { Home: IconHome, Histogram: IconHistogram, Live: IconLive, Setting: IconSetting };
    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = 'color: var(--semi-color-text-2); margin-inline-end: 12px';
</script>

{#snippet sideHeader()}
    <NavHeader text="Semi Design">
        {#snippet logo()}
            <IconSemiLogo style="font-size: 36px" />
        {/snippet}
    </NavHeader>
{/snippet}

{#snippet topFooter()}
    <NavFooter>
        <Button aria-label="通知" style={actionStyle} theme="borderless">
            {#snippet icon()}
                <IconBell size="large" />
            {/snippet}
        </Button>
        <Button aria-label="帮助" style={actionStyle} theme="borderless">
            {#snippet icon()}
                <IconHelpCircle size="large" />
            {/snippet}
        </Button>
        <Avatar color="orange" size="small">YJ</Avatar>
    </NavFooter>
{/snippet}

<Layout style="border: 1px solid var(--semi-color-border)">
    <LayoutSider style="background-color: var(--semi-color-bg-1)">
        <Nav aria-label="侧边导航" footer={{ collapseButton: true }} header={sideHeader} {items} selectedKeys={['Home']} style="max-width: 220px; height: 100%">
            {#snippet icon({ item })}
                {@const Icon = icons[item.itemKey as keyof typeof icons]}
                <Icon size="large" />
            {/snippet}
        </Nav>
    </LayoutSider>
    <Layout>
        <LayoutHeader style="background-color: var(--semi-color-bg-1)">
            <Nav aria-label="顶部导航" footer={topFooter} mode="horizontal" />
        </LayoutHeader>
        <LayoutContent style="padding: 24px; background-color: var(--semi-color-bg-0)">
            <Breadcrumb {routes} style="margin-bottom: 24px" />
            <div style="border-radius: 10px; border: 1px solid var(--semi-color-border); height: 376px; padding: 32px">
                <Skeleton loading>
                    {#snippet placeholder()}
                        <SkeletonParagraph rows={2} />
                    {/snippet}
                    <p>Hi, Bytedance dance dance.</p>
                    <p>Hi, Bytedance dance dance.</p>
                </Skeleton>
            </div>
        </LayoutContent>
        <LayoutFooter style="display: flex; justify-content: space-between; padding: 20px; color: var(--semi-color-text-2); background-color: var(--semi-grey-0)">
            <span style="display: flex; align-items: center">
                <IconBytedanceLogo size="large" style="margin-inline-end: 8px" />
                <span>Copyright © 2019 ByteDance. All Rights Reserved. </span>
            </span>
            <span>
                <span style="margin-inline-end: 24px">平台客服</span>
                <span>反馈建议</span>
            </span>
        </LayoutFooter>
    </Layout>
</Layout>

API Reference

Layout

Props

PropertyDescriptionTypeDefault
childrenContent of the Layout: LayoutHeader, LayoutSider, LayoutContent, LayoutFooter or a nested Layout.Snippet-
hasSiderIndicates that a Sider is inside. A Sider among the direct children is detected automatically; a deeper one registers after mount, so pass true during server rendering to avoid style flashing. Passing false cannot remove the has-sider class that a Sider adds.boolean-
prefixClsClass name prefix.string"semi-layout"
tagNameTag name of the root element.keyof HTMLElementTagNameMap"section"

class, style and native attributes (including role and aria-*) land on the root element.

Layout.Header, Layout.Footer, Layout.Content

The three share one API. Their default tags are header, footer and main.

Layout.Header

Props

PropertyDescriptionTypeDefault
childrenContent of the header.Snippet-
prefixClsClass name prefix.string"semi-layout"
tagNameTag name of the root element.keyof HTMLElementTagNameMap"header"
typeFor internal use; decides the class name suffix.string"header"

Props

PropertyDescriptionTypeDefault
childrenContent of the footer.Snippet-
prefixClsClass name prefix.string"semi-layout"
tagNameTag name of the root element.keyof HTMLElementTagNameMap"footer"
typeFor internal use; decides the class name suffix.string"footer"

Layout.Content

Props

PropertyDescriptionTypeDefault
childrenContent of the main area.Snippet-
prefixClsClass name prefix.string"semi-layout"
tagNameTag name of the root element.keyof HTMLElementTagNameMap"main"
typeFor internal use; decides the class name suffix.string"content"

class, style and native attributes (including role and aria-*) land on each root element.

Layout.Sider

Props

PropertyDescriptionTypeDefault
breakpointBreakpoints that trigger the responsive layout: xs, sm, md, lg, xl or xxl. Read once at mount.TypeArray<keyof ResponsiveMap>-
childrenContent of the sider.Snippet-
onBreakpointCalled once at mount for each screen listed in breakpoint with the current viewport state, then whenever it starts or stops matching, with the screen name and whether it matches.Type(screen: keyof ResponsiveMap, match: boolean) => void-
prefixClsClass name prefix.string"semi-layout"

class, style and native attributes and events (including role, aria-* and onclick) land on the root aside element.

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 renders an aside; pass aria-label to describe what this Sider is for.
  • Header and Footer render header and footer by default. Inside the section of a Layout they are not banner or contentinfo landmarks; pass role="banner" or role="contentinfo" when they are the header or footer of the whole page.
  • Content renders main by default, and a page should have only one main; when nesting, use tagName to switch to another tag.
  • Header, Content and Footer accept role and aria-label to describe the corresponding element.