设计协作

暗色模式

能力介绍

大多数情况下,深色模式是浅色模式的补充。默认选用值,更多取决于用户的审美选择或业务场景,用户可以根据自己的需要选择使用哪一个模式。

🤩 Semi 的全部颜色变量都有亮色与暗色两套取值,可以方便地进行切换。
🌒 Semi 也支持在页面的局部范围使用亮/暗色模式。

推荐设置

Semi 会自动在根元素(:root)上挂载全局色盘,我们内置了一些常用的 CSS Token,详细的 Token 详情可查阅 设计变量
我们推荐你在 body 上配置 color、background-color, 你的业务组件可从 body 自动继承获得默认的背景色、文本颜色,自适应亮/暗色切换

// css
body {
    color: var(--semi-color-text-0);
    background-color: var(--semi-color-bg-0);
}

如何切换

Semi 暗色模式的切换是通过给 body 添加属性 [theme-mode='dark'] 来实现的(亮色色盘声明在 :root 上,暗色色盘在带该属性的元素上重新声明)。属性加在任意祖先元素上都可以生效。暗色值只在带属性的元素及其后代上,读取 --semi-* 变量时请在 body 或其后代上读取。你可以使用任何你喜欢的方式来进行切换。比如:

const body = document.body;
if (body.hasAttribute('theme-mode')) {
    body.removeAttribute('theme-mode');
} else {
    body.setAttribute('theme-mode', 'dark');
}

这里也有一个🌰:

代码
<script setup lang="ts">
    import { Button } from '@fibrous/semi-ui-vue';

    const switchMode = () => {
        const body = document.body;
        if (body.hasAttribute('theme-mode')) {
            body.removeAttribute('theme-mode');
        } else {
            body.setAttribute('theme-mode', 'dark');
        }
    };
</script>

<template>
    <Button @click="switchMode">Switch Mode</Button>
</template>

定制两种模式的颜色

在应用的样式中覆写 --semi-* 颜色变量即可,亮色值写在 :root 上,暗色值写在 [theme-mode='dark'] 上。Semi 的样式位于 @layer semi 中,应用中不分层的规则总是优先,不需要 !important:

:root {
    --semi-color-primary: var(--semi-violet-5);
}

[theme-mode='dark'] {
    --semi-color-primary: var(--semi-violet-4);
}

使用局部亮/暗色模式时,.semi-always-light、.semi-always-dark 元素会重新声明颜色变量;希望覆写也作用到这些区域时,把这两个类名分别加进上面的选择器。

和系统主题保持一致

如果你希望页面的亮色/暗色模式能自动和系统主题保持一致,可以参考 prefers-color-scheme 属性。

macOS 下的系统主题可以通过 系统偏好设置 -> 通用 -> 外观 来配置。

你可以通过 JS 的方式监听该属性的变化,这里也有一个🌰:

const mql = window.matchMedia('(prefers-color-scheme: dark)');

function matchMode(e) {
    const body = document.body;
    if (e.matches) {
        if (!body.hasAttribute('theme-mode')) {
            body.setAttribute('theme-mode', 'dark');
        }
    } else {
        if (body.hasAttribute('theme-mode')) {
            body.removeAttribute('theme-mode');
        }
    }
}

matchMode(mql);
mql.addEventListener('change', matchMode);

局部暗色/亮色模式

Semi 2.0 原生支持局部暗色/亮色模式。使用时,在顶级元素上添加 .semi-always-dark 或 .semi-always-light 类,这个类下的组件会使用对应模式的颜色变量。




Finished
This is a description.
2
In Progress
This is a description.
3
Waiting
This is a description.

Finished
This is a description
In Process
This is a description
3
Waiting
This is a description
XZ5
YB5
LX5
YZ
HW
XM

grey tag
blue tag
ghost tag
solid tag
red tag
green tag
orange tag
teal tag
violet tag
white tag

I am Popover
I am Tooltip

  • 审核中
    2019-07-14 10:35
  • 发布成功
    2019-06-13 16:17
  • 审核失败
    2019-05-14 18:34
Copyright © 2019 ByteDance. All Rights Reserved. 平台客服反馈建议
代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { IconBell, IconBytedanceLogo, IconCamera, IconEdit, IconHelpCircle, IconHistogram, IconHome, IconList, IconLive, IconSemiLogo, IconSetting } from '@fibrous/semi-icons-vue';
    import { Avatar, Badge, Breadcrumb, Button, Layout, Nav, Pagination, Popover, Rating, Row, Steps, Tag, Timeline, Tooltip, type NavItemConfig, type NavItems } from '@fibrous/semi-ui-vue';

    const { Header, Footer, Sider, Content } = Layout;
    const mode = ref('semi-always-dark');

    const switchMode = () => {
        mode.value = mode.value === 'semi-always-dark' ? 'semi-always-light' : 'semi-always-dark';
    };

    const rowStyle = { margin: '16px 10px' };
    const badgeStyle = { width: '42px', height: '42px', borderRadius: '4px', display: 'inline-block' };
    const tagStyle = { marginRight: '8px', marginBottom: '8px' };
    const headerButtonStyle = { color: 'var(--semi-color-text-2)', marginRight: '12px' };

    const sideItems: NavItems = [
        { itemKey: 'Home', text: '首页' },
        { itemKey: 'Histogram', text: '基础数据' },
        { itemKey: 'Live', text: '测试功能' },
        { itemKey: 'Setting', text: '设置' },
    ];
    const sideIcons = { Home: IconHome, Histogram: IconHistogram, Live: IconLive, Setting: IconSetting };
    const sideIconOf = (item: NavItemConfig) => sideIcons[item.itemKey as keyof typeof sideIcons];

    const exampleItems: NavItems = [
        { itemKey: 'user', text: 'Option1' },
        { itemKey: 'union', text: 'Option2' },
        { itemKey: 'approve-management', text: 'Group3', items: ['3-1', '3-2'] },
    ];
    const exampleIcons = { user: IconEdit, union: IconCamera, 'approve-management': IconList };
    const exampleIconOf = (item: NavItemConfig) => exampleIcons[item.itemKey as keyof typeof exampleIcons];

    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const tags = [
        { color: 'grey', label: 'grey tag' },
        { color: 'blue', label: 'blue tag' },
        { color: 'blue', type: 'ghost', label: 'ghost tag' },
        { color: 'blue', type: 'solid', label: 'solid tag' },
        { color: 'red', label: 'red tag' },
        { color: 'green', label: 'green tag' },
        { color: 'orange', label: 'orange tag' },
        { color: 'teal', label: 'teal tag' },
        { color: 'violet', label: 'violet tag' },
        { color: 'white', label: 'white tag' },
    ] as const;
</script>

<template>
    <Button :style="{ marginBottom: '4px' }" @click="switchMode">Switch Content Mode</Button>
    <Layout :style="{ border: '1px solid var(--semi-color-border)' }">
        <Header :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
            <div>
                <Nav aria-label="顶部导航" :defaultSelectedKeys="['Home']" mode="horizontal">
                    <Nav.Header>
                        <IconSemiLogo :style="{ width: '96px', height: '36px', fontSize: '36px' }" />
                    </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 aria-label="通知" :style="headerButtonStyle" theme="borderless">
                            <template #icon>
                                <IconBell size="large" />
                            </template>
                        </Button>
                        <Button aria-label="帮助" :style="headerButtonStyle" theme="borderless">
                            <template #icon>
                                <IconHelpCircle size="large" />
                            </template>
                        </Button>
                        <Avatar color="orange" size="small">YJ</Avatar>
                    </Nav.Footer>
                </Nav>
            </div>
        </Header>
        <Layout>
            <Sider :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
                <Nav aria-label="侧边导航" :defaultSelectedKeys="['Home']" :footer="{ collapseButton: true }" :items="sideItems" :style="{ maxWidth: '220px', height: '100%' }">
                    <template #icon="{ item }">
                        <component :is="sideIconOf(item)" size="large" />
                    </template>
                </Nav>
            </Sider>
            <Content :class="mode" :style="{ padding: '24px', backgroundColor: 'var(--semi-color-bg-1)' }">
                <Breadcrumb :routes="routes" :style="{ marginBottom: '24px' }" />
                <div
                    :style="{
                        borderRadius: '10px',
                        border: '1px solid var(--semi-color-border)',
                        height: '700px',
                        padding: '32px',
                    }"
                >
                    <Row :style="rowStyle">
                        <Nav aria-label="示例导航" :items="exampleItems" mode="horizontal">
                            <template #icon="{ item }">
                                <component :is="exampleIconOf(item)" />
                            </template>
                        </Nav>
                        <br />
                        <br />
                        <Pagination showSizeChanger :total="80" />
                        <br />
                        <Steps :current="1">
                            <Steps.Step>
                                <template #title>Finished</template>
                                <template #description>This is a description.</template>
                            </Steps.Step>
                            <Steps.Step>
                                <template #title>In Progress</template>
                                <template #description>This is a description.</template>
                            </Steps.Step>
                            <Steps.Step>
                                <template #title>Waiting</template>
                                <template #description>This is a description.</template>
                            </Steps.Step>
                        </Steps>
                        <br />
                        <Steps :current="1" status="error">
                            <Steps.Step>
                                <template #title>Finished</template>
                                <template #description>This is a description</template>
                            </Steps.Step>
                            <Steps.Step>
                                <template #title>In Process</template>
                                <template #description>This is a description</template>
                            </Steps.Step>
                            <Steps.Step>
                                <template #title>Waiting</template>
                                <template #description>This is a description</template>
                            </Steps.Step>
                        </Steps>
                    </Row>
                    <Row :style="rowStyle">
                        <div :style="{ display: 'flex' }">
                            <div :style="{ padding: '8px' }">
                                <Badge :count="5" theme="solid">
                                    <Avatar color="blue" shape="square" :style="badgeStyle">XZ</Avatar>
                                </Badge>
                            </div>
                            <div :style="{ padding: '8px' }">
                                <Badge :count="5" theme="light">
                                    <Avatar color="cyan" shape="square" :style="badgeStyle">YB</Avatar>
                                </Badge>
                            </div>
                            <div :style="{ padding: '8px' }">
                                <Badge :count="5" theme="inverted">
                                    <Avatar color="indigo" shape="square" :style="badgeStyle">LX</Avatar>
                                </Badge>
                            </div>
                            <div :style="{ padding: '8px' }">
                                <Badge dot theme="solid">
                                    <Avatar color="light-blue" shape="square" :style="badgeStyle">YZ</Avatar>
                                </Badge>
                            </div>
                            <div :style="{ padding: '8px' }">
                                <Badge dot theme="light">
                                    <Avatar color="teal" shape="square" :style="badgeStyle">HW</Avatar>
                                </Badge>
                            </div>
                            <div
                                :style="{
                                    padding: '8px',
                                    borderRadius: '4px',
                                    backgroundColor: 'var(--semi-color-fill-0)',
                                }"
                            >
                                <Badge dot theme="inverted">
                                    <Avatar color="green" shape="square" :style="badgeStyle">XM</Avatar>
                                </Badge>
                            </div>
                        </div>
                        <br />
                        <div>
                            <Tag v-for="tag in tags" :key="tag.label" :color="tag.color" :style="tagStyle" :type="'type' in tag ? tag.type : undefined">
                                {{ tag.label }}
                            </Tag>
                        </div>
                        <br />
                        <div :style="{ display: 'flex', alignItems: 'center' }">
                            <Popover :style="{ padding: '8px' }">
                                <Tag :style="{ marginRight: '8px' }">I am Popover</Tag>
                                <template #content>hi semi-design</template>
                            </Popover>
                            <Tooltip>
                                <Tag :style="{ marginRight: '8px' }">I am Tooltip</Tag>
                                <template #content>hi semi-design</template>
                            </Tooltip>
                            <Rating :defaultValue="3" size="small" :style="{ marginRight: '8px' }" />
                        </div>
                        <br />
                        <Timeline>
                            <Timeline.Item type="ongoing">
                                <template #time>2019-07-14 10:35</template>
                                审核中
                            </Timeline.Item>
                            <Timeline.Item type="success">
                                <template #time>2019-06-13 16:17</template>
                                发布成功
                            </Timeline.Item>
                            <Timeline.Item type="error">
                                <template #time>2019-05-14 18:34</template>
                                审核失败
                            </Timeline.Item>
                        </Timeline>
                    </Row>
                </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 © 2019 ByteDance. All Rights Reserved. </span>
            </span>
            <span>
                <span :style="{ marginRight: '24px' }">平台客服</span>
                <span>反馈建议</span>
            </span>
        </Footer>
    </Layout>
</template>