You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 18.2.11自定义M3主题实现明暗模式切换方案求助

适配Angular 18.2.11的自定义M3明暗主题切换方案

问题背景

使用Angular 18.2.11 + Angular Material + 自定义M3主题,已在theme-consumer.scss中定义明暗主题,但styles.scss固定加载浅色主题,尝试通过@media (prefers-color-scheme: dark)应用暗色主题时触发重复定义错误,需要实现适配系统明暗模式偏好+支持手动切换自定义调色板的方案。

解决方案步骤

1. 完善theme-consumer.scss主题定义

确保明暗主题使用自定义调色板,且变量可被外部访问:

// theme-consumer.scss
@use 'sass:map';
@use '@angular/material' as mat;

// 自定义调色板(保留你生成的完整内容)
$_palettes: (
  primary: (
    0: #000000,
    10: #001e2e,
    20: #06344a,
    // ... 剩余调色板值
  ),
  tertiary: (
    // ... 你的 tertiary 调色板值
  )
);

// 合并默认对比度值(保持原有逻辑)
$_rest: (
  contrast: (
    0: #ffffff,
    10: #ffffff,
    // ... 对应contrast值
  )
);
$_primary: map.merge(map.get($_palettes, primary), $_rest);
$_tertiary: map.merge(map.get($_palettes, tertiary), $_rest);

// 定义明暗主题,使用!global让外部可引用
$light-theme: mat.define-theme((
  color: (
    theme-type: light,
    primary: $_primary,
    tertiary: $_tertiary,
  ),
)) !global;

$dark-theme: mat.define-theme((
  color: (
    theme-type: dark,
    primary: $_primary,
    tertiary: $_tertiary,
  ),
)) !global;

2. 修改styles.scss:适配系统偏好+支持手动切换

移除重复的主题定义,通过:root和类选择器分别绑定明暗主题,同时通过媒体查询自动适配系统偏好:

// styles.scss
@use '@angular/material' as mat;
@use './common.scss';
@use './theme-consumer.scss';
@import "bootstrap/dist/css/bootstrap.min.css";
@import "@fortawesome/fontawesome-free/css/all.min.css";

// 仅调用一次mat.core()
@include mat.core();

// 1. 默认加载浅色主题
:root {
  @include mat.all-component-themes($light-theme);
}

// 2. 手动切换暗色主题时的类样式
.dark-theme {
  @include mat.all-component-themes($dark-theme);
}

// 3. 适配系统暗色模式偏好,仅在未手动设置主题时生效
@media (prefers-color-scheme: dark) {
  :root:not(.light-theme) {
    @include mat.all-component-themes($dark-theme);
  }
}

// 自定义组件样式,跟随主题切换自动适配
.tertiary-radio {
  @include mat.radio-color($light-theme, $color-variant: tertiary);
  .dark-theme & {
    @include mat.radio-color($dark-theme, $color-variant: tertiary);
  }
}

// 应用排版样式,跟随主题切换
@include mat.typography-hierarchy($light-theme);
.dark-theme {
  @include mat.typography-hierarchy($dark-theme);
}

3. 添加Angular组件内的手动切换逻辑

创建主题服务控制html元素类名,同时保存用户偏好:

// theme.service.ts
import { Injectable, Renderer2, RendererFactory2 } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ThemeService {
  private renderer: Renderer2;
  private htmlElement: HTMLElement;

  constructor(rendererFactory: RendererFactory2) {
    this.renderer = rendererFactory.createRenderer(null, null);
    this.htmlElement = document.documentElement;
    
    // 初始化:优先读取本地存储,其次适配系统偏好
    const savedTheme = localStorage.getItem('theme');
    if (savedTheme) {
      this.setTheme(savedTheme as 'light' | 'dark');
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
      this.setTheme('dark');
    }
  }

  setTheme(theme: 'light' | 'dark') {
    this.htmlElement.classList.remove('light-theme', 'dark-theme');
    this.htmlElement.classList.add(`${theme}-theme`);
    localStorage.setItem('theme', theme);
  }

  toggleTheme() {
    const currentTheme = this.htmlElement.classList.contains('dark-theme') ? 'light' : 'dark';
    this.setTheme(currentTheme);
  }
}

在组件中调用切换逻辑:

// app.component.ts
import { Component } from '@angular/core';
import { ThemeService } from './theme.service';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="themeService.toggleTheme()">切换明暗主题</button>
    <!-- 其他组件内容 -->
  `
})
export class AppComponent {
  constructor(public themeService: ThemeService) {}
}

关键说明

  • 用!global导出主题变量,确保styles.scss能正确引用
  • 通过:root和类选择器避免重复定义错误,媒体查询仅在未手动设置主题时生效
  • 本地存储保存用户主题偏好,优先级高于系统默认设置

内容的提问来源于stack exchange,提问作者parascus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 14:17:33