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.
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.
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:
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ủ đề.
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.
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.
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.
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.tsx và Hero.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
Ở đâ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>✓</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ủ đề:
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:
Khi lặp lại mảng themes này, chúng tôi nhận được kết quả sau trên DOM.
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 MediaQueryList — prefersDark và prefersLight. 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 prefersDark và prefersLight. 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ủ đề.
Đâ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>
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: