Tạo chủ đề với các biến Tailwind CSS

Khám phá cách sử dụng các thuộc tính tùy chỉnh CSS của Tailwind và một kiến ​​trúc rõ ràng để tạo các chủ đề linh hoạt và dễ bảo trì cho các dự án web của bạn.

Hiểu kiến ​​trúc sạch trong chủ đề

Khi phát triển ứng dụng, các nguyên tắc nền tảng như nguyên tắc mã hóa SOLID và DRY tỏ ra rất quan trọng. Những nguyên tắc này không chỉ định hình cấu trúc mã mà còn ảnh hưởng đến các chủ đề và tích hợp thiết kế giao diện người dùng.

Nguyên tắc RẮN cho phép các nhà phát triển đảm bảo rằng mỗi thành phần có một vai trò cụ thể. Điều này tạo điều kiện cho việc triển khai thiết kế giao diện người dùng và chủ đề dễ dàng hơn. Tương tự, nguyên tắc DRY nhấn mạnh đến khả năng sử dụng lại, dẫn đến kiến ​​trúc rõ ràng trong chủ đề.

Hiểu cách các nguyên tắc này liên quan đến chủ đề bao gồm việc xem xét vai trò của chúng. Điều này bao gồm các vai trò trong việc tạo ra các ứng dụng có khả năng thích ứng với các chiến lược theo chủ đề có cấu trúc tốt. Những nguyên tắc này đóng vai trò là trụ cột dẫn đường cho các nhà phát triển, cho phép tạo ra các ứng dụng mạnh mẽ nhằm giải quyết một cách hiệu quả các yêu cầu về chủ đề ngày càng phát triển.

Tận dụng các biến CSS để tạo chủ đề trong Tailwind

Các biến CSS đóng vai trò then chốt trong Tailwind. Họ cung cấp một cách tiếp cận năng động để quản lý chủ đề một cách hiệu quả. Tính linh hoạt của chúng cho phép sửa đổi nhanh chóng mà không cần thay đổi nhiều về mã, từ đó nâng cao khả năng xử lý các chủ đề đa dạng của Tailwind.

Việc sử dụng các biến CSS trong Tailwind mang lại những lợi ích vốn có. Đặc biệt, nó hỗ trợ tổ chức các giá trị chủ đề như màu sắc, phông chữ và khoảng cách. Cách tiếp cận tập trung này hợp lý hóa việc quản lý chủ đề, đảm bảo cập nhật có hệ thống và có tổ chức.

Lợi ích của các biến CSS đối với chủ đề động rất đa dạng, bao gồm:

  • điều chỉnh chủ đề nhanh chóng cho chủ đề kép và đa chủ đề
  • tạo và quản lý hiệu quả nhiều chủ đề trong dự án
  • một quy trình tạo chủ đề được sắp xếp hợp lý để dễ dàng tùy chỉnh và thích ứng
  • tạo điều kiện thuận lợi cho các yêu cầu thiết kế đa dạng mà không cần thay đổi mã rộng rãi

Trong một dự án mẫu sắp tới, chúng tôi sẽ thể hiện sự hội tụ của các yếu tố này. Phần trình diễn này kết hợp các nguyên tắc kiến ​​trúc sạch và ứng dụng của chúng vào các ứng dụng theo chủ đề.

Triển khai thực tế: Thiết lập dự án

Chúng tôi bắt đầu bằng cách tạo ứng dụng React bằng cách sử dụng Vite và thêm TypeScript. Bạn có thể chọn sử dụng Tạo ứng dụng React nếu muốn. Chúng tôi cài đặt CSS Tailwind để tạo kiểu và giao diện.

Để bắt đầu dự án, chúng tôi sẽ thiết lập React Vite, một công cụ cực nhanh dành cho các ứng dụng React. Nếu bạn chưa cài đặt, hãy cài đặt nó trên toàn cầu bằng cách sử dụng npm hoặc yarn.

yarn install
Discover how to use Tailwind CSS custom properties and a clean architecture to create flexible and maintainable themes for your web projects
yarn global add create-vite

Sử dụng React Vite để tạo dự án mới có hỗ trợ TypeScript. Bạn có thể đổi tên variables-theme-app bằng tên dự án bạn muốn. Bạn cũng có thể chọn các tính năng bạn cần khi được Vite nhắc trong dòng lệnh:

create-vite variables-theme-app .

Sau đó, truy cập thư mục dự án bằng lệnh này:

cd variables-theme-app

Bạn có thể khởi động máy chủ phát triển ngay bây giờ để xem trước ứng dụng của mình:

yarn run dev

Truy cập URL phát triển cục bộ trong trình duyệt của bạn. Thực hiện theo quá trình cài đặt Tailwind CSS từ hướng dẫn chính thức.

Xây dựng giao diện người dùng

Bây giờ chúng ta hãy xây dựng một trang đích người dùng mẫu. Đây là nơi chúng tôi sẽ triển khai chủ đề với các biến CSS và CSS của Tailwind.

Cấu hình CSS và biểu định kiểu Tailwind

Trước tiên, chúng tôi định cấu hình các biến Tailwind trên tailwind.config.js. Sau đó, chúng tôi cập nhật biểu định kiểu index.css của mình:

//tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {

      //We define our color variable here to be assigned a value on the stylesheet

      colors: {
        accent: {
          1: "var(--accent1)",
        },
        baseOne: "var(--baseOne)",
        baseTwo: "var(--baseTwo)",
        baseThree: "var(--baseThree)",
        baseFour: "var(--baseFour)",
      },
    },
  },
  plugins: [],
}

Từ đối tượng màu tailwind.config.js, chúng tôi xác định các biến màu tùy chỉnh. Gắn liền với mỗi biến là một tên và giá trị cụ thể. Ví dụ: accent là nhóm màu có sắc thái được biểu thị bằng 1, được gán giá trị từ biến CSS --accent1.

Các biến màu khác được gán giá trị trực tiếp từ các biến CSS tương ứng. Đây là --baseOne, --baseTwo, v.v., để sử dụng trong biểu định kiểu.

Chúng tôi xác định các biến màu này bằng cách sử dụng các thuộc tính (biến) tùy chỉnh CSS để cho phép tạo chủ đề linh hoạt. Điều này cũng cho phép truy cập vào các điều chỉnh màu dễ dàng trong suốt biểu định kiểu. Chúng đóng vai trò giữ chỗ đề cập đến các giá trị màu cụ thể. Do đó, cho phép sử dụng màu nhất quán trên toàn bộ ứng dụng. Họ cũng áp dụng các thay đổi cho những màu này từ vị trí trung tâm là index.css.

Các biến này sau đó được xác định trên biểu định kiểu index.css:

//index.css

@layer base {
  :root {
    --baseOne: hsl(0, 0%, 100%);
    --baseTwo: hsl(252, 2%, 51%);
    --baseThree: hsl(0, 0%, 96%);
    --baseFour: hsl(0, 0%, 100%);
    --accent1: hsl(6, 100%, 80%);
  }

  @media (prefers-color-scheme: dark) {
    :root {
      --baseOne: hsl(229, 57%, 11%);
      --baseTwo: hsl(243, 100%, 93%);
      --baseThree: hsl(228, 56%, 26%);
      --baseFour: hsl(229, 57%, 11%);
      --accent1: hsl(6, 100%, 80%);
    }
  }

  :root[data-theme="dark"] {
    --baseOne: hsl(229, 57%, 11%);
    --baseTwo: hsl(243, 100%, 93%);
    --baseThree: hsl(228, 56%, 26%);
    --baseFour: hsl(229, 57%, 11%);
    --accent1: hsl(6, 100%, 80%);
  }

  :root[data-theme="light"] {
    --baseOne: hsl(0, 0%, 100%);
    --baseTwo: hsl(252, 2%, 51%);
    --baseThree: hsl(0, 0%, 96%);
    --baseFour: hsl(0, 0%, 100%);
    --accent1: hsl(6, 100%, 80%);
  }

  :root[data-theme="third"] {
    --baseOne: hsl(167, 56%, 22%);
    --baseTwo: hsl(140, 69%, 40%);
    --baseThree: hsl(0, 0%, 0%);
    --baseFour: hsl(0, 3%, 13%);
    --accent1: hsl(6, 100%, 80%);
  }

Mã CSS này xác định các biến màu cho các chủ đề khác nhau: mặc định, tối, sáng và thứ ba. Nó sử dụng các thuộc tính tùy chỉnh CSS (--baseOne, --baseTwo, v.v.) để gán các giá trị màu cụ thể. Chủ đề thay đổi dựa trên tùy chọn bảng màu của thiết bị hoặc thuộc tính dữ liệu (data-theme). Chúng cũng được áp dụng cho thành phần tài liệu.

Giao diện người dùng trang đích

Tiếp theo, chúng tôi tạo các thành phần cần thiết để tạo nên giao diện người dùng trang đích. Đây là các thành phần Header.tsxHero.tsx:

//Header.tsx
const Header = () => {
    return (
        <header className='flex items-center justify-between py-4 shadow shadow-gray-200 bg-baseOne transition-colors duration-300 lg:px-[160px] sm:px-[40px] px-[16px]'>
            <div>
                <img className="w-[40px]" src={heroIcon} alt="icon" />
            </div>
            <nav className="sm:block hidden">
                <ul className='flex items-center space-x-5'>
                    <li><a href="#">Home</a></li>
                    <li><a href="#">About</a></li>
                    <li><a href="#">Contact Us</a></li>
                </ul>
            </nav>
            <div>
                <button><strong>Select Theme</strong></button>
            </div>
        </header>
    );
};

export default Header;

Từ Header.tsx ở trên, chúng ta tạo phần tiêu đề của trang đích. Phần này chứa các liên kết giả và trình kích hoạt để hiển thị hoặc ẩn các mẫu chủ đề.

Tiếp theo là phần Hero.tsx. Chúng tôi tạo kiểu cho nó bằng Tailwind và cung cấp một ít thông tin về nội dung của bài viết:

//Hero.tsx
import heroIcon from '../assets/png/hero.png'

const Hero = () => {
  return (
    <section className="lg:px-[160px] sm:px-[40px] px-[16px]">
      <div className='flex sm:flex-row flex-col items-start justify-between sm:pt-32 pt-12 sm:text-left text-center'>
        <aside className='max-w-[550px]'>
          <h2 className='sm:text-5xl text-3xl'>Theming With CSS Variables</h2>
          <p className='pt-5'>Customizing themes using CSS Variables alongside Tailwind CSS offers a flexible way to style web applications. CSS Variables enable easy theme adjustments, while Tailwind CSS's utility classes simplify and speed up the styling process for consistent designs.</p>
        </aside>
        <aside className='sm:w-auto w-full sm:block flex items-center justify-center sm:pt-0 pt-10'>
          <img className='min-w-[300px]' src={heroIcon} alt="icon" />
        </aside>
      </div>
    </section>
  )
}

export default Hero

Tiếp theo, chúng tôi nhập các thành phần này vào tệp cơ sở của mình App.tsx. Vì vậy, trang đích tĩnh của chúng tôi có bố cục cơ bản mà không có bất kỳ chức năng hoặc chủ đề bổ sung nào:

import Header from "./components/Header"
import Hero from "./components/Hero"

function App() {
  return (
    <main>
      <Header />
      <Hero />
    </main>
  )
}
export default App
trang đích

Giao diện người dùng và chức năng của mẫu trình chuyển đổi chủ đề

Ở đây, chúng tôi xây dựng giao diện người dùng mẫu chủ đề và thêm các chức năng tương ứng của chúng. Mục tiêu của thành phần này là cung cấp cho người dùng quyền truy cập vào các chủ đề được lựa chọn.

Đầu tiên, chúng tôi tạo thành phần giao diện người dùng ThemeSwitcher.tsx:

//ThemeSwitcher.tsx
import { useState } from 'react';
import light from '../assets/svg/light.svg';
import dark from '../assets/svg/dark.svg';
import third from '../assets/svg/third.svg';

type Theme = {
  src: string;
  alt: string;
  name: string;
};

const themes: Theme[] = [
  { src: light, alt: 'Light', name: 'light' },
  { src: dark, alt: 'Dark', name: 'dark' },
  { src: third, alt: 'Third', name: 'third' },
];

const ThemeSwitcher = () => {
  const [selectedTheme, setSelectedTheme] = useState('');

  const handleThemeChange = (themeName: string) => {
    setSelectedTheme(themeName);
  };

  return (
    <section className='bg-baseThree px-5 py-4 absolute lg:right-36 sm:right-12 right-4 top-24'>
      <div className='grid sm:grid-cols-3 grid-cols-1 gap-10'>
        {themes.map((theme, index) => (
          <div className={`max-w-[150px] p-1 ${selectedTheme === theme.name && 'border-2 border-green-500 rounded-md'}`} key={index}>
            <label>
              <input
                type='radio'
                name='theme'
                value={theme.name}
                checked={selectedTheme === theme.name}
                onChange={() => handleThemeChange(theme.name)}
                className='hidden'
              />
              <img className='rounded-md cursor-pointer' src={theme.src} alt={theme.alt} />
              <div className='flex items-center justify-between mt-2'>
                <h5 className='capitalize text-sm text-baseTwo'>{theme.name} Mode</h5>
                <div className='bg-green-500 rounded-full w-[20px] flex items-center justify-center text-white text-sm'>{selectedTheme === theme.name && <span>&#10003;</span>}</div>
              </div>
            </label>
          </div>
        ))}
      </div>
    </section>
  );
};

export default ThemeSwitcher;

Trong đoạn mã ở trên, chú thích ThemeSwitcher.tsx xác định cấu trúc được gọi là Theme. Cấu trúc này có ba phần: src, alt, và name. Loại này nhằm mục đích an toàn loại, chỉ định cấu trúc đối tượng cho các chủ đề:

  • src: string
  • alt: string
  • name: string

Các thuộc tính này đảm bảo định dạng nhất quán cho các đối tượng chủ đề trong cơ sở mã.

Sau khi xác định cấu trúc này để đảm bảo an toàn về kiểu, chúng ta đã khởi tạo mảng themes. Nó chứa các đối tượng phù hợp với cấu trúc kiểu Theme được xác định này. Điều này đảm bảo rằng mỗi đối tượng chủ đề tuân theo định dạng được chỉ định trong ứng dụng:

  • src: vị trí của hình ảnh hoặc tài nguyên liên quan đến chủ đề đó
  • alt: mô tả cho hình ảnh được sử dụng trong chủ đề
  • name: tên riêng cho từng chủ đề

Khi lặp lại mảng themes này, chúng tôi nhận được kết quả sau trên DOM.

mẫu chủ đề

Tiếp theo, chúng tôi thêm các chức năng cập nhật chủ đề. Cái này vẫn nằm trong ThemeSwitcher.tsx:

//ThemeSwitcher.tsx
 useEffect(() => {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
    const prefersLight = window.matchMedia('(prefers-color-scheme: light)');

    const updateTheme = () => {
      const storedTheme = localStorage.getItem('selectedTheme');
      const setTheme = (theme: string) => {
        document.documentElement.setAttribute('data-theme', theme);
        setSelectedTheme(theme);
      };

      if (storedTheme !== null) {
        setTheme(storedTheme);
      } else {
        switch (true) {
          case prefersDark.matches:
            setTheme('dark');
            break;
          case prefersLight.matches:
            setTheme('light');
            break;
          default:
            break;
        }
      }
    };

    updateTheme();

    prefersDark.addEventListener('change', updateTheme);
    prefersLight.addEventListener('change', updateTheme);

    return () => {
      prefersDark.removeEventListener('change', updateTheme);
      prefersLight.removeEventListener('change', updateTheme);
    };
  }, []);

Đây là nơi điều kỳ diệu xảy ra. Hàm useEffect này xử lý logic chủ đề dựa trên bảng màu ưa thích. Nó khởi tạo hai đối tượng MediaQueryListprefersDarkprefersLight. Những cách phối màu tối và sáng này nhắm đến mục tiêu.

Phần quan trọng là việc gọi setTheme(). Điều này đặt thuộc tính data-theme trên document.documentElement. Thuộc tính này thay đổi chủ đề của ứng dụng để phù hợp với sở thích của người dùng.

Chúng tôi gọi updateTheme() để đặt chủ đề. Sau đó, trình xử lý sự kiện sẽ được thêm vào prefersDarkprefersLight. Mục đích của việc này là để theo dõi những thay đổi trong sở thích về bảng màu. Khi những tùy chọn này thay đổi, hàm updateTheme() sẽ kích hoạt tương ứng.

Cuối cùng, chức năng dọn dẹp sẽ loại bỏ trình xử lý sự kiện khi thành phần ngắt kết nối. Điều này đảm bảo xử lý rõ ràng các bản cập nhật chủ đề dựa trên tùy chọn phối màu.

Thành phần này được nhập vào Header.tsx, nơi đặt nút chuyển đổi hiển thị của nó. Khi chọn bất kỳ chủ đề nào, chủ đề màu tương ứng sẽ được cập nhật. Vì vậy, chúng ta có thể chọn chỉ thực hiện các lựa chọn chủ đề kép hoặc nhiều chủ đề.

lựa chọn chủ đề

So sánh

Đây là điều sẽ xảy ra khi chúng ta không tuân theo các nguyên tắc kiến ​​trúc sạch mà chúng ta đã thảo luận. Nhìn vào đoạn mã bên dưới, rõ ràng tùy chọn đầu tiên tốt hơn nhiều.

Bây giờ, hãy nghĩ đến việc áp dụng tùy chọn thứ hai cho một dự án lớn:

//With clean architecture
<div className="bg-baseOne text-baseThree">
    Comparison
</div>

//Without
<div className="bg-gray-100 dark:bg-gray-600 third:bg-yellow-500 text-gray-800 dark:text-gray-200 third:text-red-500">
    Comparison
</div>

Thực hành tốt nhất

Dưới đây là một số hướng dẫn hữu ích giúp công việc trở nên dễ dàng và hiệu quả hơn. Những đề xuất này có thể cải thiện cách chúng tôi tạo và quản lý dự án, giúp việc duy trì và đảm bảo hiệu suất tốt hơn trở nên đơn giản hơn:

  • Xóa cách đặt tên. Nâng cao khả năng đọc bằng các quy ước đặt tên nhất quán.
  • Mô-đun hóa. Chia mã thành các mô-đun có thể sử dụng lại để mở rộng quy mô.
  • Nội dung được tối ưu hóa. Tăng tốc thời gian tải bằng phương tiện được tối ưu hóa.
  • Tiêu chuẩn về khả năng tiếp cận. Đảm bảo thiết kế phù hợp với nhu cầu hỗ trợ tiếp cận.
  • Thử nghiệm trên nhiều trình duyệt. Xác nhận tính nhất quán trên các trình duyệt và thiết bị.
  • Đánh giá mã thường xuyên. Đảm bảo chất lượng thông qua việc đánh giá mã định kỳ.

#tailwindcss 

Tạo chủ đề với các biến Tailwind CSS
1.30 GEEK