Rumah  >  Artikel  >  hujung hadapan web  >  Pemilih Tema CSS dengan Mod Automatik [Tutorial]

Pemilih Tema CSS dengan Mod Automatik [Tutorial]

Linda Hamilton
Linda Hamiltonasal
2024-10-08 22:08:30828semak imbas

Tutorial ini menunjukkan kepada anda cara membuat pemilih tema dalam Svelte, mendayakan berbilang pilihan tema untuk tapak web anda. Ia juga termasuk tema automatik yang menyesuaikan diri dengan tetapan peranti pengguna.

Walaupun ia dilaksanakan dalam Svelte, kebanyakan fungsi bergantung pada HTML dan CSS standard, menjadikannya mudah untuk direplikasi dalam rangka kerja lain.

Kod sumber untuk projek boleh didapati di sini: https://github.com/ivarnm/theme_selector

Demo langsung: https://theme-selector-inm.vercel.app/

Menyediakan projek:

Jika anda belum mempunyai projek Svelte sedia ada, anda boleh menciptanya dengan mengikuti panduan permulaan Svelte: https://svelte.dev/docs/introduction

Tambah pembolehubah warna:

Kami akan menggunakan pembolehubah CSS untuk menentukan tema warna kami. Mula-mula, buat fail src/styles/global.css dan tambahkan CSS berikut:

:root {
  --neutral-0: white;
  --neutral-10: #fffcf1;
  --neutral-30: #d2d2d2;
  --neutral-40: #b7b7b7;
  --neutral-60: #666666;
  --neutral-70: #333333;
  --neutral-100: black;

  --primary-60: #696d90;
  --primary-70: #3D405B;
  --primary-80: #303349;

  --secondary-70: #5a7b6b;
  --secondary-80: #456153;
}

.dark-theme {
  --neutral-0: black;
  --neutral-10: #1a1a1a;
  --neutral-30: #3d3d3d;
  --neutral-40: #595959;
  --neutral-60: #999999;
  --neutral-70: #cccccc;
  --neutral-100: white;

  --primary-60: #979ec7;
  --primary-70: #a7aed6;
  --primary-80: #b8bfe9;

  --secondary-70: #79BEA5;
  --secondary-80: #89cfb5;
}

.warm-theme {
  --neutral-0: #fff7e0;
  --neutral-10: #ffedcc;
  --neutral-30: #ffdbb7;
  --neutral-40: #ffb89d;
  --neutral-60: #ff9473;
  --neutral-70: #ff5733;
  --neutral-100: #4d2600;

  --primary-60: #f28e2b;
  --primary-70: #d65a31;
  --primary-80: #c44536;

  --secondary-70: #e59572;
  --secondary-80: #cf6448;
}

html {
  font-family: 'Inter', sans-serif;
  background-color: var(--neutral-10);
}

body {
  margin: 0 auto;
  max-width: 870px;
  color: var(--neutral-70);
}

Ini mentakrifkan palet warna untuk tema terang pada elemen akar dan menambah tema gelap dan hangat. Warna dan tema yang anda inginkan, sudah tentu, terpulang kepada anda. Ini hanyalah beberapa contoh. Anda juga melihat cara kami boleh menggunakan pembolehubah ini untuk menetapkan warna latar belakang halaman dan sifat warna badan.

Seterusnya, tambahkan fail src/routes/ layout.svelte dan tambah kod berikut untuk mengimport fail CSS secara global:

<script>
  import "../styles/global.css";
</script>

Buat menu lungsur turun:

Saya lebih suka mempunyai pemilih tema saya dalam menu lungsur, jadi saya akan membuat komponen DropdownMenu boleh guna semula yang boleh kami gunakan. Jika anda ingin melakukan perkara yang sama, anda boleh mencipta fail src/lib/components/DropdownMenu.svelte dan menambah yang berikut:

<script lang="ts">
  let menuOpen = false;

  const toggleMenu = () => {
    menuOpen = !menuOpen;
  };

  const handleDropdownFocusLoss = (event: FocusEvent) => {
    const focusedElement = event.relatedTarget instanceof HTMLElement ? event.relatedTarget : null;
    const menuElement = event.currentTarget instanceof HTMLElement ? event.currentTarget : null;

    // Check if the new focus is inside the menu
    if (focusedElement && menuElement && menuElement.contains(focusedElement)) {
        return;
    }

    menuOpen = false;
  };
</script>

<div class="container">
  <div class="menu" on:focusout={handleDropdownFocusLoss}>
    <button class="icon-btn" on:click={toggleMenu}>
      <slot name="icon"></slot>
    </button>

    {#if menuOpen}
      <div class="dropdown">
        <slot name="dropdown"></slot>
      </div>
    {/if}
  </div>
</div>

<style>
  .menu {
    position: relative;
  }

  .icon-btn {
    background-color: transparent;
    border: none;
    cursor: pointer;
    padding: 6px;
    margin: 0;
    border-radius: 50%;
    overflow: hidden;
    width: 49px;
    height: 49px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .icon-btn:hover {
    outline: none;
    background-color: var(--neutral-30);
  }

  .dropdown {
    position: absolute;
    top: 60px;
    right: 0;
    background-color: var(--neutral-30);
    border: 1px solid var(--neutral-40);
    color: var(--neutral-100);
    padding: 5px;
    z-index: 100;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: 200px;
  }

  .dropdown :global(button) {
    padding: 10px 15px;
    font-size: 20px;
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
  }

  .dropdown :global(button:hover) {
    background-color: var(--neutral-40);
  }
</style>

Buat komponen pemilih tema:

Kini tiba masanya untuk mencipta komponen yang akan mengendalikan pemilihan tema. Cipta fail
src/lib/components/ThemeSelector.svelte dan tambahkan yang berikut:

<script lang="ts">
  import { onMount } from 'svelte';
  import { writable } from 'svelte/store';
  import DropdownMenu from '$lib/components/DropdownMenu.svelte';

  // If you want to use the theme variable in other components, you can move it to a dedicated ts/js file and import it here instead
  let theme = writable<string>("system");

  // Define your themes and their names.
  const THEMES = [
      { value: 'system', label: 'Automatic' },
      { value: 'light', label: 'Light' },
      { value: 'dark', label: 'Dark' },
      { value: 'warm', label: 'Warm' },
  ];

  onMount(() => {
    // Prevents the code from running on the server
    if (typeof window == 'undefined') return;

    let storedTheme = localStorage.getItem('theme');

    // Get the user's system theme preference
    let systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; 

    if (storedTheme && THEMES.some(themeOption => themeOption.value === storedTheme)) {
      theme.set(storedTheme);
    } 
    else {
      theme.set('system');
      applyTheme(systemTheme);
    }

    // Update the automatic theme when the system theme changes if the theme is set to automatic
    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
      systemTheme = e.matches ? 'dark' : 'light';
      if (storedTheme === 'system') {
        applyTheme(systemTheme);
      }
    });

    theme.subscribe(value => {
      if (value === 'system') {
        applyTheme(systemTheme);
      } 
      else {
        applyTheme(value);
      }

      localStorage.setItem('theme', value);
      storedTheme = value;
    });
  });

  function applyTheme(theme: string) {
    document.documentElement.className = "";
    if(theme === 'system') return;

    // Add the theme class (e.g dark-theme) to the document element to apply the theme
    document.documentElement.classList.add(`${theme}-theme`);
  }

</script>

<DropdownMenu>
  <svg slot="icon" width="37" height="37" viewBox="0 0 37 37" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M29 18.5C29 24.299 24.299 29 18.5 29C12.701 29 8 24.299 8 18.5C8 12.701 12.701 8 18.5 8C24.299 8 29 12.701 29 18.5Z" stroke="currentColor" stroke-width="2"/>
    <path d="M9 28L6.17157 30.8284" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
    <path d="M1 18.5H5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
    <path d="M9 9L6.17157 6.17157" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
    <path d="M18.5 5L18.5 1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
    <path d="M28 9L30.8284 6.17157" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
    <path d="M32 18.5H36" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
    <path d="M28 28L30.8284 30.8284" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
    <path d="M18.5 36L18.5 32" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
    <circle cx="14.5" cy="18.5" r="6.5" stroke="currentColor" stroke-width="2"/>
    <line x1="8.03459" y1="22.0512" x2="17.227" y2="12.8588" stroke="currentColor" stroke-width="2"/>
    <line x1="11.0346" y1="25.0512" x2="20.227" y2="15.8588" stroke="currentColor" stroke-width="2"/>
  </svg>  

  <div slot="dropdown">
    {#each THEMES as themeOption}
      <button 
        class="theme-button" 
        class:selected={$theme === themeOption.value}
        on:click={() => $theme = themeOption.value}
      >
        <svg width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <circle cx="12" cy="12" r="7" fill="{$theme === themeOption.value ? 'var(--primary-60)' : 'transparent'}"/>
        </svg>
        <span>{themeOption.label}</span>
      </button>
    {/each}
  </div>
</DropdownMenu>

<style>
  :global(svg) {
    color: var(--neutral-70);
  }

  .theme-button.selected {
    font-weight: bold;
  }

  button {
    margin: 0;
    padding: 0;
    border: none;
    border-radius: 4px;
    background-color: transparent;
    color: inherit;
    cursor: pointer;
  }
</style>

Apabila komponen dimuatkan dalam penyemak imbas, kami menyemak sama ada pengguna telah memilih tema sebelum ini. Jika tidak ia lalai kepada tema automatik. Tema automatik menetapkan tema kepada sama ada terang atau gelap menggunakan pertanyaan media prefers-color-scheme untuk mengesan jika pengguna telah meminta tema warna terang atau gelap dalam OS mereka.

Apabila menetapkan tema, contohnya hangat, kelas tema hangat ditambah pada elemen akar halaman, yang akan mengatasi pembolehubah warna CSS kepada yang kita ditakrifkan sebelum ini dalam pemilih .warm-theme dalam fail global.css kami.

Menambah pemilih tema:

Kini kami boleh menambah komponen ThemeSelector pada fail susun atur kami. Tukar kandungan fail src/routes/ layout.svelte kepada ini:

<script>
  import '../styles/global.css';
  import ThemeSelector from '$lib/components/ThemeSelector.svelte';
</script>

<div>
  <header>
    <ThemeSelector />
  </header>
  <main>
    <slot />
  </main>
</div>

<style>
  header {
    display: flex;
    justify-content: flex-end;
  }

  main {
    margin: 50px 0;
  }
</style>

Ini akan menambah lungsur pemilih tema ke bahagian atas sebelah kanan semua halaman.

Tambahkan beberapa contoh kandungan untuk melihat tema:

Dalam fail src/routes/ page.svelte kita boleh menambah beberapa kotak untuk melihat tema warna:

<h1>Theme selector</h1>

<div class="box-container">
  <div class="box neutral-0">--neutral-0</div>
  <div class="box neutral-10">--neutral-10</div>
  <div class="box neutral-30">--neutral-30</div>
  <div class="box neutral-40">--neutral-40</div>
  <div class="box neutral-60">--neutral-60</div>
  <div class="box neutral-70">--neutral-70</div>
  <div class="box neutral-100">--neutral-100</div>
  <div class="box primary-60">--primary-60</div>
  <div class="box primary-70">--primary-70</div>
  <div class="box primary-80">--primary-80</div>
  <div class="box secondary-70">--secondary-70</div>
  <div class="box secondary-80">--secondary-80</div>
</div>

<style>
  .box-container {
    display: flex;
    gap: 30px;
    flex-wrap: wrap;
  }

  .box {
    width: 150px;
    height: 150px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .neutral-0 {
    background-color: var(--neutral-0);
    color: var(--neutral-100);
  }

  .neutral-10 {
    background-color: var(--neutral-10);
    color: var(--neutral-100);
    border: 5px solid var(--neutral-30);
    box-sizing: border-box;
  }

  .neutral-30 {
    background-color: var(--neutral-30);
    color: var(--neutral-100);
  }

  .neutral-40 {
    background-color: var(--neutral-40);
    color: var(--neutral-100);
  }

  .neutral-60 {
    background-color: var(--neutral-60);
    color: var(--neutral-0);
  }

  .neutral-70 {
    background-color: var(--neutral-70);
    color: var(--neutral-0);
  }

  .neutral-100 {
    background-color: var(--neutral-100);
    color: var(--neutral-0);
  }

  .primary-60 {
    background-color: var(--primary-60);
    color: var(--neutral-0);
  }

  .primary-70 {
    background-color: var(--primary-70);
    color: var(--neutral-0);
  }

  .primary-80 {
    background-color: var(--primary-80);
    color: var(--neutral-0);
  }

  .secondary-70 {
    background-color: var(--secondary-70);
    color: var(--neutral-0);
  }

  .secondary-80 {
    background-color: var(--secondary-80);
    color: var(--neutral-0);
  }
</style>

Dan inilah keputusan akhir

CSS Theme Selector with Automatic Mode [Tutorial]

Atas ialah kandungan terperinci Pemilih Tema CSS dengan Mod Automatik [Tutorial]. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn