CSS自定义属性在组件开发中的使用
在图解CSS系列的《CSS自定义属性》一文中,对CSS的自定义属性做过深入的阐述。如果你阅读过这篇文章,应该对CSS自定义属性有所了解,也能体会到该特性的强大之处。今天,CSS自定义属性可以用于Web开发中的很多地方,能让开发者变得更为便捷灵活,比如最近流行的Dark Mode开发。而今天我们就来和大家聊聊CSS自定义属性在组件开发中的运用。
为什么要在组件开发中使用CSS自定义属性
对于Web开发者而言,组件开发已经有很多种模式,从传统的HTML和CSS模式到现代JavaScript框架(比如React,Vue)。不管是哪一种方式,都离不开CSS,而且只是CSS的使用方式不同而以。
如果你还在使用传统的组件开发模式,可能你更趋向于将CSS放在一个独立的.css
文件中,并且将文件和组件放在一起:
而使用JavaScript框架开发时,可能会将CSS和模板都放在同一个.js
(或.vue
)文件中:
特别是使用React框架开发时,很多开发都都热衷于CSS-in-JS的模式。
在React中写样式有很多种方式,前段时间《React中编写CSS的姿势》一文有专门介绍过,感兴趣的同学可以看看。
不管是哪种方式,都有其利弊,这里不会和大家深聊他们之间的差异,主要和大家一起探讨CSS自定义属性如何使我们的工作流得到进一步的改善。并且探讨自定义属性在React和Vue框架构建组件时怎么使用。
很多Web开发者都这么认为:
不需要CSS中的变量(自定义属性),因为可以使用JavaScript做完所有事情。
事实上,在使用框架开发组件时,使用CSS自定义属性有两个主要原因:
- 人体工程学很好
- 它开启了新的可能性,可以使用CSS自定义属性做一些JavaScript做不到的事情
既然如此,那我们就开始吧。
快速回顾CSS自定义属性
CSS自定义更深入的介绍,请移步这里阅读。
这里我们只是快速回顾CSS自定义属性的基础知识。在使用CSS自定义属性时,一般会在:root
中先声明CSS自定义属性:
:root {
--color-text: black;
--color-background: lightgray;
--color-primary: rebeccapurple;
--gutter: 16px;
}
然后在需要使用的地方,通过var()
函数来调用已声明的自定义属性,通过var()
函数调用的自定义属性会作为别的CSS属性的值,比如:
.element {
color: var(--color-text);
margin-bottom: var(--gutter);
}
.primary {
color: var(--color-primary);
}
它们看上去就像属性,事实上也是如此。通过--
前缀声明的被称为属性,即CSS自定义属性;被var()
函数调用的自定义属性(即--
前缀声明的自定义属性)又被称为是CSS的变量。也可以说:
CSS自定义属性也被称为CSS变量。换句话说,它既是属性也是变量。
在CSS中,CSS自定义属性可以像其他CSS属性一样,可以运用于任何HTML元素上。也同样遵从CSS的使用规则。
使用CSS自定义属性构建Web组件
我想你对CSS自定义属性有一个基本的认识了,即使没有也不必着急,接下来我将一步一步带大家进入CSS自定义的事件。
下面我将拿一个常见的组件为例,向大家介绍CSS自定义属性在组件构建中的使用。比如我们要构建下面这样的一个卡片组件:
上面的Demo根据@Alan在Codepen上的案例改来的。
这个Demo并不复杂,上面示例也是我们传统的一种做法,一看代码便知道怎么回事。接下来,我将会使用CSS自定义属性来改造上面的卡片,为了节约时间,只将颜色部分换成CSS自定义属性描述。
正如上图所示,简单的分析卡片上各个元素使用到的颜色,大约有九个地方,共用了七种颜色,这样一来,我们显式的在:root
中声明七个有关于颜色的自定义属性:
:root {
--primary-color: #ffffff; /* 主色 */
--body-bg-color: #f1f1f1; /* body背景颜色 */
--card-bg-color: var(--primary-color); /* card背景颜色 */
--card-box-shadow-color: #405070; /* card盒子阴影颜色 */
--btn-bg-color: #28c3f5; /* button背景颜色 */
--btn-color: var(--primary-color); /* button文本颜色 */
--paragraph-color: gray; /* 段落文本颜色 */
--card-object-bg-color: #eaeff8; /* card顶部背景颜色 */
--avatar-bg-color: var(--primary-color); /* 头像背景颜色 */
--title-color: #101c34; /* 标题2文本颜色 */
}
注意,自定义属性的名称可以根据自己的喜好来定义,但建议使用具有语义化的名称。
这样一来,我们上面的案例可以换成自定义属性,并且在运用到颜色的属性换成var()
函数,引用已声明的自定义属性:
body {
background-color: var(--body-bg-color);
}
.card {
background: var(--card-bg-color);
box-shadow: 0px 1px 10px 1px var(--card-box-shadow-color);
}
.card__object {
background-color: var(--card-object-bg-color);
}
.card__avatar {
background-color: var(--avatar-bg-color);
}
.card__body {
background: var(--card-bg-color);
}
.card__body h4 {
color: var(--title-color);
}
.card__body p {
color: var(--paragraph-color);
}
.card__body .btn {
background: var(--btn-bg-color);
color: var(--btn-color);
}
.card__body .btn:hover {
color: var(--btn-bg-color);
}
你可能已经发现了,上面我们有的自定义属性嵌套了另一个自定义属性。在CSS自定义属性中,是不推荐这么用的。正如上例所示,在用到#ffffff
颜色的地方,统一使用一个自定义属性,比如--primary-color
。
就上面而言,你或许还没有发现CSS自定义的优势。下面这个示例,可以向你展示它的优势。我们在上例的基础上,复制另一个卡片出来,但我们期望的主题色不一样:
就上面的示例而言,我们没有做太多的调整,只是将卡片放在不同的两个容器中:
<!-- HTML -->
<div class="wrapper light">
<div class="card">...</div>
</div>
<div class="wrapper dark">
<div class="card">...</div>
</div>
在.dark
卡片上,只是调整重新定义一份自定义属性的值:
.dark {
--color: #fff;
--primary-color: #1a1515;
--body-bg-color: #1a1818;
--card-box-shadow-color: #6a716e;
--btn-bg-color: #ff5722;
--paragraph-color: #c7c1c1;
--card-object-bg-color: #282035;
--title-color: #ffffff;
--avatar-bg-color: #673ab7;
}
是不是觉得好香。如果你接触过Dark Mode的话,你可能已经感觉到了,我们可以让同一个卡片,根据用户系统的喜好设置来显示卡片的风格。这个时候将会用到CSS的最新媒体查询prefers-color-scheme
,即,将.drak
块中的自定义属性放到该媒体查询中:
@media (prefers-color-scheme: dark) {
:root {
/* Dark Card */
--color: #fff;
--primary-color: #1a1515;
--body-bg-color: #1a1818;
--card-box-shadow-color: #6a716e;
--btn-bg-color: #ff5722;
--paragraph-color: #c7c1c1;
--card-object-bg-color: #282035;
--title-color: #ffffff;
--avatar-bg-color: #673ab7;
}
}
这个时候,你看到的效果会是像下面这样:
你可以尝试着切换系统设置:
就可以看到卡片亮色和暗色的切换。你也可以直接通过Chrome的开发者调试工具对两种模式进行切换:
在dark
和light
两者之间切换,你就能看到Dark Mode两种模式下卡片的效果:
我们还可以在此基础上做得更好一些,比如说添加一个切换按钮,让用户可以直接在页面中选择自己喜欢的主题:
上面我们看到的是最简单的方式。如果你对颜色系统熟悉的话,那么还可以使用CSS自定义属性、CSS的颜色系统(比如HSL
)和CSS的calc()
函数结合起来调整颜色的色相、饱和度和亮度来改变颜色,从而达到我们给组件换肤的效果。如果你对这方面感兴趣的话,可以阅读@Dieter Raber的新博文《Creating Color Themes With Custom Properties, HSL, and a Little calc()》。文章就详细介绍了,如何使用自定义属性来实现换肤效果:
如果你对颜色相关的知识感兴趣的话,可以花点时间阅读下面几篇文章:
有了这个基础之后,我们就可以进入JavaScript框架构建组件怎么使用CSS自定义属性了。我们先来看React中怎么使用CSS自定义属性。
React组件中使用CSS自定义属性
在这里不会和大家聊怎么用React构建组件和如何在React中使用CSS。我想你在这方面应该有一定的基础,甚至比我强。如果你和我一样是React的初学者,建议你先花一点点时间阅读下面几篇文章:
其实在去年整理过一篇怎么在React构建组件时使用CSS自定义属性。
在接下来的内容中,将会和大家探讨不同的使用方式。注意,接下来构建React组件都是基于Create React App方式来构建。
我们继续吧。请在命令终端先执行下面的命令来构建一个React项目:
❯ npx create-react-app react-css-custom-property
在该项目中,创建一个名为Card
组件,并且将上面Demo中卡片移动到React中来。
在Card
组件中,我们采用的还是样式和模板分离的方式,为了更好的管理CSS自定义属性,在这个Demo中将所有声明的CSS自定义属性放置在/src/cssVar.css
中:
// src/cssVar.css
:root {
--color: #333;
--primary-color: #ffffff; /* 主色 */
--body-bg-color: #f1f1f1; /* body背景颜色 */
--card-box-shadow-color: #405070; /* card盒子阴影颜色 */
--btn-bg-color: #28c3f5; /* button背景颜色 */
--paragraph-color: gray; /* 段落文本颜色 */
--card-object-bg-color: #eaeff8; /* card顶部背景颜色 */
--title-color: #101c34; /* 标题2文本颜色 */
--avatar-bg-color: #fff;
}
@media (prefers-color-scheme: dark) {
:root {
--color: #fff;
--primary-color: #1a1515; /* 主色 */
--body-bg-color: #1a1818; /* body背景颜色 */
--card-box-shadow-color: #6a716e; /* card盒子阴影颜色 */
--btn-bg-color: #ff5722; /* button背景颜色 */
--paragraph-color: #c7c1c1; /* 段落文本颜色 */
--card-object-bg-color: #282035; /* card顶部背景颜色 */
--title-color: #ffffff; /* 标题2文本颜色 */
--avatar-bg-color: #673ab7;
}
}
看到的效果将是我们预期想要的:
在我们实际的开发中,同一个组件可能会用于不同的页面或者说同一页面上不同位置,并且在样式上有一定的差异化。为了向大家演示这样的一个场景,我们在同一文件中引用两个相同的Card
组件,只是让另一个Card
组件在样式上有差异。首先尝试着在新的Card
组件中改变已声明的自定义属性的值:
// /src/App.css
.wrapper.aside {
--body-bg-color: #cdcaca;
--color: #03a9f4;
}
.aside .card {
--primary-color: #ffffff;
--card-box-shadow-color: #151817;
}
.aside .card__object {
--card-object-bg-color: #9e9e9e;
}
.aside .card__avatar {
--avatar-bg-color: #2196f3;
}
.aside .card__body h4 {
--title-color: #000000;
}
.aside .card__body p {
--paragraph-color: #7a7575;
}
.aside .card__body .btn {
--btn-bg-color: #03a9f4;
--primary-color: #ffffff;
}
.aside .card__body .btn:hover {
--btn-bg-color: #03a9f4;
}
引用在.aside
中Card
组件在UI上已经达到我们想要的效果:
但很多React开发者并不太喜欢上面的开发模式,因为上面的开发模式并不能解决CSS存在的问题,比如说样式的冲突。为了避免无必要的烦恼,因此会采用CSS-in-JS的开发模式。
在使用CSS-in-JS模式的时候,有可能会依赖于一些优秀的库,比如说styled-components。因此,就先从这种方式开始。
CSS自定义属性和styled-components的结合
styled-components是一个非常优秀的库。
如果要使用该库来构建React组件,首先需要先安装它:
❯ npm install --save styled-components
同样拿Card
组件为例,为了更好的区分前面的示例,我们重新创建一个名为CardWithStyledComponents
的组件:
// src/components/CardWithStyledComponents
import React from 'react';
import styled from 'styled-components';
interface CardProps {
title: string;
userName: string;
avatarUrl: string;
des: string;
buttonText: string;
}
const Card = styled.div`
width: 30vw;
min-height: 60vh;
border-radius: 8px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
`;
const CardObject = styled.div`
height: 15vh;
width: 100%;
border-radius: 8px 8px 0 0;
display: flex;
justify-content: center;
`;
const CardAvatar = styled.div`
width: 80px;
height: 80px;
border-radius: 50%;
padding: 5px;
transform: translateY(calc(15vh - 40px));
`;
const CardAvatarImg = styled.img`
width: 80px;
height: 80px;
border-radius: 50%;
`;
const CardBody = styled.div`
min-height: 28vh;
padding: 50px 20px 40px;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
border-radius: 0 0 8px 8px;
`;
const UserName = styled.h3`
box-sizing: border-box;
line-height: 1.6;
font-weight: bold;
`;
const CardTitle = styled.h4`
box-sizing: border-box;
line-height: 1.6;
font-weight: bold;
opacity: 0.4;
`;
const CardDes = styled.p`
font-size: 16px;
margin-bottom: 30px;
`;
const CardLink = styled.a`
padding: 12px 20px;
border: none;
border-radius: 32px;
font-size: 12px;
text-decoration: none;
font-weight: bold;
transition: all 0.1s ease-in;
&:hover {
background: transparent;
border: 1px solid currentColor;
}
`;
const CardWithStyledComponents = (props: CardProps) => {
const {title, userName, avatarUrl, des, buttonText, ...rest} = props
return (
<Card {...rest}>
<CardObject>
<CardAvatar>
<CardAvatarImg src={avatarUrl} alt={userName} />
</CardAvatar>
</CardObject>
<CardBody>
<UserName>{userName}</UserName>
<CardTitle>{title}</CardTitle>
<CardDes>{des}</CardDes>
<div className="action">
<CardLink href="https://www.fedev.cn">{buttonText}</CardLink>
</div>
</CardBody>
</Card>
)
}
export default CardWithStyledComponents;
并在App.tsx
中引用CardWithStyledComponents
组件:
<CardWithStyledComponent
title="Digital Product Designer"
avatarUrl="https://media-exp1.licdn.com/dms/image/C5603AQG9JCcSU89gTg/profile-displayphoto-shrink_200_200/0?e=1592438400&v=beta&t=HIWU0K4mFvIm5eAXPvb7wps7qKDY4MoUV1S6wPeZl30"
userName="Alan Dong"
des="Build it in a scalable way, we moved slow to move fast."
buttonText="View Profile"
/>
在浏览器中,看到的卡片只有布局的效果,但并没有颜色方面的样式:
现在我们要做的是将颜色添加到卡片的元素上。为了能更好管理颜色,我们在src/
目录下创建一个constants.ts
文件,声明一个COLORS
对象,把卡片组件要用到的颜色都放在这里:
// src/constants.ts
export const COLORS = {
color: "#333",
primaryColor: "#ffffff",
bodyBgColor: "#f1f1f1",
cardBoxShadowColor: "#405070",
btnBgColor: "#28c3f5",
paragraphColor: "gray",
cardObjectBgColor: "#eaeff8",
titleColor: "#101c34",
avatarBgColor: "#fff",
};
将管理颜色的文件在卡片组件中引用,并且将颜色赋值到对应的CSS属性之上:
// src/components/CardWithStyledComponents/index.tsx
import { COLORS } from '../../constants';
const Card = styled.div`
// ...
background: ${COLORS.primaryColor};
box-shadow: 0px 1px 10px 1px ${COLORS.cardBoxShadowColor};
`;
const CardObject = styled.div`
// ...
background-color: ${COLORS.cardObjectBgColor};
`;
const CardAvatar = styled.div`
// ...
background-color: ${COLORS.avatarBgColor};
`;
const UserName = styled.h3`
color: ${COLORS.color};
`;
const CardTitle = styled.h4`
// ...
color: ${COLORS.titleColor};
`;
const CardDes = styled.p`
// ...
color: ${COLORS.paragraphColor};
`;
const CardLink = styled.a`
// ...
background: ${COLORS.btnBgColor};
color: ${COLORS.primaryColor};
&:hover {
// ...
color: ${COLORS.btnBgColor}
}
`;
如果不出意外,你在浏览器看到的效果会像下面这样:
我们还可以使用styled-components
的ThemeProvider
特性将theme
(props
)透传给子组件。将CardWithStyledComponents
复制一份并粘贴到src/
目录下,然后将其命名为CardWithStyledComponentsDark
,向大家演示ThemeProvider
改变组件UI,实现一个暗色系卡片。在这个新组件中,要改变的地方很少:
// src/components/CardWithStyledComponentsDark/index.tsx
const Card = styled.div`
background: ${props => props.theme.colors.primaryColor};
box-shadow: 0px 1px 10px 1px ${props => props.theme.colors.cardBoxShadowColor};
`;
const CardObject = styled.div`
background-color: ${props => props.theme.colors.cardObjectBgColor};
`;
const CardAvatar = styled.div`
background-color: ${props => props.theme.colors.avatarBgColor};
`;
const UserName = styled.h3`
color: ${props => props.theme.colors.color};
`;
const CardTitle = styled.h4`
color: ${props => props.theme.colors.titleColor};
`;
const CardDes = styled.p`
color: ${props => props.theme.colors.paragraphColor};
`;
const CardLink = styled.a`
background: ${props => props.theme.colors.btnBgColor};
color: ${props => props.theme.colors.primaryColor};
&:hover {
color: ${props => props.theme.colors.btnBgColor}
}
`;
并在App.tsx
中引入ThemeProvider
和黑色系卡片要的颜色对象DARK_COLORS
:
// src/App.tsx
import CardWithStyledComponentsDark from './components/CardWithStyledComponentsDark'
import { ThemeProvider } from 'styled-components';
import { DARK_COLORS } from './constants';
<ThemeProvider theme={{colors: DARK_COLORS}}>
<CardWithStyledComponentsDark
title="Web UI Developer"
avatarUrl="https://avatars0.githubusercontent.com/u/368462?s=460&v=4"
userName="Airen Liao"
des="When others mention you, assign you, or request your review, GitHub will let them know that you have limited availability."
buttonText="Edit profile"
/>
</ThemeProvider>
效果如下:
你可能已经发现了,上面的几个效果并没有使用CSS自定义属性。嗯,没错。不过不用着急,接下来这个示例就来向大家演示在styled-components
中如何使用CSS的自定义属性。这里将会使用到styled-components
的createGlobalStyle
特性。为了更好的演示效果,先复制CardWithStyledComponentsDark
组件,并将其重新命名为CardWithStyledComponentsVars
。并且在组件中使用到UI颜色的属性值换成CSS自定义属性:
// src/components/CardWithStyledComponentsVars/index.tsx
const Card = styled.div`
background: var(--primary-color);
box-shadow: 0px 1px 10px 1px var(--card-box-shadow-color);
`;
const CardObject = styled.div`
background-color: var(--card-object-bg-color);
`;
const CardAvatar = styled.div`
transform: translateY(calc(15vh - 40px));
background-color: var(--avatar-bg-color);
`;
const UserName = styled.h3`
color: var(--color);
`;
const CardTitle = styled.h4`
color: var(--title-color);
`;
const CardDes = styled.p`
color: var(--paragraph-color);
`;
const CardLink = styled.a`
background: var(--btn-bg-color);
color: var(--primary-color);
&:hover {
color: var(--btn-bg-color)
}
`;
// src/App.tsx
import CardWithStyledComponentsVars from './components/CardWithStyledComponentsVars'
import { createGlobalStyle } from 'styled-components';
const GlobalStyles = createGlobalStyle`
html {
--color: #333;
--primary-color: #ffffff;
--body-bg-color: #f1f1f1;
--card-box-shadow-color: #405070;
--btn-bg-color: #28c3f5;
--paragraph-color: gray;
--card-object-bg-color: #eaeff8;
--title-color: #101c34;
--avatar-bg-color: #fff;
}
`
function App() {
return (
<div className="app">
<GlobalStyles />
<div className="wrapper">
<CardWithStyledComponentsVars
title="Digital Product Designer"
avatarUrl="https://media-exp1.licdn.com/dms/image/C5603AQG9JCcSU89gTg/profile-displayphoto-shrink_200_200/0?e=1592438400&v=beta&t=HIWU0K4mFvIm5eAXPvb7wps7qKDY4MoUV1S6wPeZl30"
userName="Alan Dong"
des="Build it in a scalable way, we moved slow to move fast."
buttonText="View Profile"
/>
</div>
</div>
);
}
这个时候你看到的示例就有CSS自定义属性的影子了:
在此基础上,将媒体查询特性@media (prefers-color-scheme: dark)
一起在GlobalStyles
中声明,就可以轻松的实现Dark Mode的效果:
const GlobalStyles = createGlobalStyle`
html {
--color: #333;
--primary-color: #ffffff;
--body-bg-color: #f1f1f1;
--card-box-shadow-color: #405070;
--btn-bg-color: #28c3f5;
--paragraph-color: gray;
--card-object-bg-color: #eaeff8;
--title-color: #101c34;
--avatar-bg-color: #fff;
}
@media (prefers-color-scheme: dark) {
:root {
--color: #fff;
--primary-color: #1a1515;
--body-bg-color: #1a1818;
--card-box-shadow-color: #6a716e;
--btn-bg-color: #ff5722;
--paragraph-color: #c7c1c1;
--card-object-bg-color: #282035;
--title-color: #ffffff;
--avatar-bg-color: #673ab7;
}
}
`
同样的,我们也可以将constants.ts
中的COLORS
和DARK_COLORS
赋值给相应的自定义属性中:
// src/App.tsx
import { COLORS, DARK_COLORS } from './constants';
const GlobalStyles = createGlobalStyle`
html {
--color: ${COLORS.color};
--primary-color: ${COLORS.primaryColor};
--body-bg-color: ${COLORS.bodyBgColor};
--card-box-shadow-color: ${COLORS.cardBoxShadowColor};
--btn-bg-color: ${COLORS.btnBgColor};
--paragraph-color: ${COLORS.paragraphColor};
--card-object-bg-color: ${COLORS.cardObjectBgColor};
--title-color: ${COLORS.titleColor};
--avatar-bg-color: ${COLORS.avatarBgColor};
}
@media (prefers-color-scheme: dark) {
:root {
--color: ${DARK_COLORS.color};
--primary-color: ${DARK_COLORS.primaryColor};
--body-bg-color: ${DARK_COLORS.bodyBgColor};
--card-box-shadow-color: ${DARK_COLORS.cardBoxShadowColor};
--btn-bg-color: ${DARK_COLORS.btnBgColor};
--paragraph-color: ${DARK_COLORS.paragraphColor};
--card-object-bg-color: ${DARK_COLORS.cardObjectBgColor};
--title-color: ${DARK_COLORS.titleColor};
--avatar-bg-color: ${DARK_COLORS.avatarBgColor};
}
}
`
有关于styled-components
更多的介绍,还可以阅读:
- React Styled Components Tutorial
- Getting the Most Out of Styled Components (7 Must Know Features)
- Converting CSS In React to Styled Components
- Playing with Styled Components
- 7 tricks to work with Styled Components
CSS自定义属性和React Context API的结合
React Context API提供了一个无需为每层组件手动添加
props
,就能在组件树间进行数据传递的方法。如果你对React Context API未接触过的话,建议你花点时间阅读《初探React Context API》一文。
或许有些开发者不太喜欢在项目中使用第三方的库,比如上面提到的styled-components
。在React开发组件的时候,不使用styled-components
也可以在组件的开发中使用CSS自定义属性。简单地说,可以使用React Context API相关的特性让CSS自定义属性在React组件中使用。
我们同样用上面提到的卡片组件为例。
首先,在src/
目录下创建一个ThemeContext.js
文件,创建一个ThemeContext
:
// src/ThemeContext.js
import React from 'react'
const ThemeContext = React.createContext('light')
export default ThemeContext
复制Card
组件,并且重新命名为CardThemeContext
。同时我们在App.js
中引用,而用将ThemeContext
导入进来。这里做一个简单的判断,如果theme
为light
引用的是COLORS
对象,如果是dark
则引用DARK_COLORS
:
const theme = useContext(ThemeContext)
const currentTheme = theme === 'light' ? COLORS : DARK_COLORS
另外,需要创建一个函数,将对象中的键值转换相应的自定义属性:
const setCSSVariables = theme => {
for (const value in theme) {
document.documentElement.style.setProperty(`--${value}`, theme[value]);
}
};
这样我们在App.js
中就可以通过ThemeContext.Provider
来调用CardThemeContext
组件:
// src/App.js
import React, { useContext, useEffect } from 'react';
import CardThemeContext from './components/CardThemeContext'
import {COLORS, DARK_COLORS} from '../src/constants'
import ThemeContext from './ThemeContext'
import './App.css';
const App = () => {
const theme = useContext(ThemeContext)
const currentTheme = theme === 'light' ? COLORS : DARK_COLORS
const setCSSVariables = theme => {
for (const value in theme) {
document.documentElement.style.setProperty(`--${value}`, theme[value]);
}
};
useEffect(()=>{
setCSSVariables(currentTheme)
})
return <ThemeContext.Provider value={theme}>
<CardThemeContext
title="Digital Product Designer"
avatarUrl="https://media-exp1.licdn.com/dms/image/C5603AQG9JCcSU89gTg/profile-displayphoto-shrink_200_200/0?e=1592438400&v=beta&t=HIWU0K4mFvIm5eAXPvb7wps7qKDY4MoUV1S6wPeZl30"
userName="Alan Dong"
des="Build it in a scalable way, we moved slow to move fast."
buttonText="View Profile"
/>
</ThemeContext.Provider>
}
export default App;
这个时候我们可以看到light
主题的样式风格:
如果把ThemeContext
的值设置为dark
:
// src/ThemeContext.js
import React from 'react'
const ThemeContext = React.createContext('light')
export default ThemeContext
那么卡片就会切换到dark
主题风格:
在上面的基础上,我们稍加改良,添加一个ToggleButton
组件,来做主题的切换:
// src/components/ToggleButton/index.js
import React from 'react'
import './index.css'
const ToggleButton = (props) => {
return <div className="bulb__container" onClick={props.clickHandler}>
<button className="bulb">
<div className="bulb__image">
<img src="https://static.fedev.cn/sites/default/files/blogs/2020/2002/bulb-on.png" alt="Swining Light Bulb" />
</div>
</button>
</div>
}
export default ToggleButton
这个时候,ThemeContext
也要做一点调整:
// src/ThemeContext.js
import React from 'react'
const ThemeContext = React.createContext(['light', () => {}])
export default ThemeContext
在App.js
添加click
事件,以及使用useState()
钩子函数来对theme
状态切换:
// src/App.js
import React, { useContext, useEffect, useState, useMemo } from 'react';
import CardThemeContext from './components/CardThemeContext'
import ToggleButton from './components/ToggleButton'
import {COLORS, DARK_COLORS} from '../src/constants'
import ThemeContext from './ThemeContext'
import './App.css';
const setCSSVariables = theme => {
for (const value in theme) {
document.documentElement.style.setProperty(`--${value}`, theme[value]);
}
};
const App = () => {
const [theme, setTheme] = useState(useContext(ThemeContext)[0])
const value = useMemo(() => ({
theme,
setTheme
}),[theme])
const clickHandler = () => {
setTheme(theme === 'light' ? 'dark' : 'light')
}
const currentTheme = theme === 'light' ? COLORS : DARK_COLORS
useEffect(()=>{
setCSSVariables(currentTheme)
})
return <ThemeContext.Provider theme={value}>
<CardThemeContext
title="Digital Product Designer"
avatarUrl="https://media-exp1.licdn.com/dms/image/C5603AQG9JCcSU89gTg/profile-displayphoto-shrink_200_200/0?e=1592438400&v=beta&t=HIWU0K4mFvIm5eAXPvb7wps7qKDY4MoUV1S6wPeZl30"
userName="Alan Dong"
des="Build it in a scalable way, we moved slow to move fast."
buttonText="View Profile"
/>
<ToggleButton clickHandler={clickHandler} />
</ThemeContext.Provider>
}
export default App;
这个时候就可以通过切换按钮让light
和dark
主题之间切换:
示例源码可以点击这里下载。
Vue中使用CSS自定义属性
在使用Vue框架来构建组件时,也可以在组件中使用CSS自定义属性,我们来看一个@Dave Follett在他的《A New Vue On JavaScript30 - 03 CSS Variables》教程中写的示例:
具体的就不展开阐述了,如果你感兴趣的话,可以阅读下面几篇文章:
- Theming using CSS Custom Properties in Vue.js Components
- A New Vue On JavaScript30 - 03 CSS Variables
- Passing Variables to CSS on a Vue Component
小结
CSS自定义属性(CSS变量)到今天为止算得止是成熟的技术了,在Web中使用的场景也越来越多。今天这篇文章主要阐述了CSS自定义属性在构建组件中是怎么使用的。最后希望这篇文章对你有所帮助。