Angular Material 3的亮色与暗色模式
我想让用户能够选择主题以及明亮或暗色模式,但在选择其他主题时让系统默认的明亮/暗色模式生效,不过我还没想清楚怎么实现。
出于某种原因,在手机上使用它们的基础 @include mat.theme 时,主题似乎没有正常工作,因此我用 mat.define-theme() 自己做了一个,但主题类型只支持light或 dark,而不支持 'light dark'。好吧,我定义了两个主题,例如 $green-theme 和 $green-theme-dark。这是我想出的一个能用于我的默认主题的做法,并且可以在body上指定 'light' 或 'dark' 类,若两者都未指定则使用系统默认:
:root {
@include my.theme(my.$azure-theme);
& .dark {
@include my.theme(my.$azure-theme-dark);
}
}
@media (prefers-color-scheme: dark) {
:root {
@include my.theme(my.$azure-theme-dark);
& .light {
@include my.theme(my.$azure-theme);
}
}
}
问题在于我还想在任意层级使用另一个类名来改变主题颜色。例如我可能会有一个主题展示页,其中包含示例HTML,显示在任何主题下的效果,甚至可能分为light和 dark的部分,这样就能在同一页面查看所有组合。为此,我希望能够在任意层级、任意顺序地定位主题名称以及light/dark。例如:
<div>
This is the current theme selected by parents
<div class="orange">this is orange with system light/dark
<div class="light">This is orange and light mode even if system is dark</div>
<div class="dark">
This is orange and dark mode even if system is light
<div class="green">
This is green and dark mode even if system is light
</div>
</div>
</div>
</div>
我不想为了 prefers-color-scheme: dark 这一节去列出几十种不同的组合,并将它们重复和互换。此外,我也不知道这样是否会让CSS变得臃肿,或者在创建它们时对浏览器性能产生影响。有没有更好的方法来实现我的需求?我的想法是让每个元素都能掌握最近祖先在主题和暗色模式上的设置,并且能够仅使用这两个值来决定从正确的主题中使用哪些变量。
我找到了 window.matchMedia('(prefers-color-scheme: dark)',我认为可以用来稍微简化一些,我的代码可以实际向body元素添加正确的类,这样我至少可以去掉CSS中的媒体查询,只需要在body应用 'light' 或 'dark' 类即可。
解决方案
我使用了这篇文章 How to set up Angular material dark theme ,它展示了如何在明暗模式之间切换。
来自MDN文档:
要启用对light-dark() 颜色函数的支持,color-scheme必须具有light dark的值,通常在 :root伪类上设置。
因此,通过定义 color-scheme: light dark;,我们可以使用 light-dark() 自动检测主题并在必要时切换。
light-dark() CSS函数接收两种颜色或两张图片,并根据活动的颜色方案返回一个颜色或一个图片,而无需使用prefers-color-scheme媒体查询。
如上文MDN所示,我们不需要 prefers-color-scheme,因为light-dark会自动处理这一点。
选项1:
我们可以设置两个类,将 color-theme 设为默认主题,这将覆盖继承的 color-theme。然后仅为这些元素定义 background-color 和 color 属性,因为对于标签 div 及其内部文本,这些属性在默认情况下是缺失的。
.dark {
color-scheme: dark;
background-color: var(--mat-sys-surface);
color: var(--mat-sys-on-surface);
}
.light {
color-scheme: light;
background-color: var(--mat-sys-surface);
color: var(--mat-sys-on-surface);
}
Stackblitz演示
选项2:
定义一个通用类 text-field,其中包含Angular Material缺失的样式,即背景颜色和颜色。
.dark {
color-scheme: dark;
}
.light {
color-scheme: light;
}
.text-field {
background-color: var(--mat-sys-surface);
color: var(--mat-sys-on-surface);
}
Stackblitz演示
现在我们看到主题作为覆盖应用,无论全局主题是明亮还是黑暗。
如果你想进一步自定义,则需要使用 mat-overrides。
.example-orange-primary-container {
@include mat.theme-overrides((
primary-container: #84ffff
));
}
主题:
@use '@angular/material' as mat;
body {
font-family: Roboto, 'Helvetica Neue', sans-serif;
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
html {
color-scheme: light dark;
@include mat.theme(
(
color: (
// Defines the primary default layout behavior
primary: mat.$violet-palette,
tertiary: mat.$orange-palette,
),
typography: Roboto,
)
);
}
body {
background-color: var(--mat-sys-surface);
color: var(--mat-sys-on-surface);
font: var(--mat-sys-body-medium);
}
.dark {
color-scheme: dark;
}
.light {
color-scheme: light;
}
.text-field {
background-color: var(--mat-sys-surface);
color: var(--mat-sys-on-surface);
}