Commit 5fb33aa

HPCesia <me@hpcesia.com>
2025-02-06 09:52:07
refactor: rewrite with daisyUI
Refactored styles using daisyUI and adapted all related components and styles.
1 parent d71123e
src/components/search/Pagefind.astro
@@ -12,15 +12,15 @@ const bundlePath = `${import.meta.env.BASE_URL}pagefind/`;
 />
 
 <template id="pagefind-result-template">
-  <a class="theme-card-bg hover:theme-card-bg-hl-trans group rounded-md p-2" href="#">
+  <a
+    class="hover:bg-primary group w-full rounded-md p-2 duration-150 hover:bg-opacity-30"
+    href="#"
+  >
     <div class="flex flex-row items-center gap-1 text-center">
-      <span class="group-hover:theme-text-hl-contrast text-lg">Fake Result</span>
-      <Icon
-        name="material-symbols:chevron-right-rounded"
-        class="theme-text-hl-contrast text-lg"
-      />
+      <span class="group-hover:text-primary text-lg duration-150">Fake Result</span>
+      <Icon name="material-symbols:chevron-right" class="text-primary text-lg" />
     </div>
-    <div id="pagefind-result-template-excerpt" class="theme-text-second">
+    <div id="pagefind-result-template-excerpt" class="text-sm opacity-60">
       This is a fake result.
     </div>
   </a>
@@ -82,6 +82,6 @@ const bundlePath = `${import.meta.env.BASE_URL}pagefind/`;
   [data-pagefind-ui] mark {
     background-color: transparent;
 
-    @apply text-[var(--theme-color-light-darken)] dark:text-[var(--theme-color-dark-lighten)];
+    @apply text-secondary;
   }
 </style>
src/components/search/SearchBaseUI.astro
@@ -1,4 +1,6 @@
 ---
+import I18nKey from '@i18n/I18nKey';
+import { i18n } from '@i18n/translation';
 import { Icon } from 'astro-icon/components';
 import type { HTMLAttributes } from 'astro/types';
 
@@ -8,29 +10,12 @@ const { class: className, ...rest } = Astro.props;
 ---
 
 <div class:list={['w-full', className]} {...rest}>
-  <div
-    class="theme-bg theme-border mb-2 flex w-full flex-row items-center gap-2 rounded-md border-2 p-4 text-center"
-  >
+  <label class="input input-bordered flex items-center gap-2">
+    <input type="text" class="grow" placeholder={i18n(I18nKey.search)} />
     <Icon name="material-symbols:search-rounded" class="text-3xl" />
-    <input type="text" class="theme-bg w-full py-1" />
-  </div>
+  </label>
   <div
-    class="search-result flex h-fit max-h-[calc(60vh-8rem)] flex-col items-center gap-2 overflow-y-auto text-center"
+    class="search-result mt-4 flex h-fit max-h-[calc(60vh-8rem)] flex-col items-center gap-2 overflow-y-auto text-center"
   >
   </div>
 </div>
-
-<style>
-  input::-webkit-outer-spin-button,
-  input::-webkit-inner-spin-button {
-    appearance: none;
-  }
-
-  input[type='number'] {
-    appearance: textfield;
-  }
-
-  input:focus {
-    outline: none;
-  }
-</style>
src/components/utils/Markdown.astro
@@ -23,20 +23,21 @@ const firstHasPre = hasPre && (isFirstInstance('md-has-pre', Astro.url) || impor
   firstHasPre && (
     <template
       id="code-toolbar-template"
-      class="code-block-wrapper relative my-4 overflow-hidden rounded-lg"
+      class="code-block-wrapper collapse-open collapse relative my-4"
     >
-      <div class="theme-border theme-card-bg-hl-trans z-10 flex items-center justify-between">
-        <Button class="toggle-btn !bg-transparent">
+      <div class="bg-primary/60 text-primary-content z-10 flex items-center justify-between">
+        <Button class="toggle-btn btn-ghost rounded-bl-none">
           <Icon
             name="material-symbols:keyboard-arrow-down-rounded"
             class="h-5 w-5 duration-300"
           />
         </Button>
         <span class="language font-mono text-sm" />
-        <Button class="copy-btn !bg-transparent">
+        <Button class="copy-btn btn-ghost rounded-br-none">
           <Icon name="material-symbols:file-copy-rounded" class="h-5 w-5 duration-300" />
         </Button>
       </div>
+      <div class="collapse-content p-0" />
     </template>
   )
 }
@@ -61,14 +62,14 @@ const firstHasPre = hasPre && (isFirstInstance('md-has-pre', Astro.url) || impor
 
       pre.parentNode?.insertBefore(wrapper, pre);
       wrapper.appendChild(toolbar);
-      wrapper.appendChild(pre);
+      wrapper.querySelector('.collapse-content')?.appendChild(pre);
 
       const toggleBtn = wrapper.querySelector('.toggle-btn');
       const copyBtn = wrapper.querySelector('.copy-btn');
 
       toggleBtn?.addEventListener('click', () => {
         toggleBtn.querySelector('svg')?.classList.toggle('-rotate-90');
-        pre.classList.toggle('hidden');
+        wrapper.classList.toggle('collapse-open');
       });
 
       const code = pre.querySelector('code');
@@ -76,16 +77,16 @@ const firstHasPre = hasPre && (isFirstInstance('md-has-pre', Astro.url) || impor
         try {
           const text = code?.textContent || '';
           await navigator.clipboard.writeText(text);
-          copyBtn?.classList.add('text-green-500');
+          copyBtn?.classList.add('text-success');
           setTimeout(() => {
-            copyBtn?.classList.remove('text-green-500');
-          }, 300);
+            copyBtn?.classList.remove('text-success');
+          }, 600);
         } catch (err) {
           console.error('Failed to copy:', err);
-          copyBtn?.classList.add('text-red-500');
+          copyBtn?.classList.add('text-error');
           setTimeout(() => {
-            copyBtn?.classList.remove('text-red-500');
-          }, 300);
+            copyBtn?.classList.remove('text-error');
+          }, 600);
         }
       });
     };
src/components/widgets/Button.astro
@@ -1,43 +1,16 @@
 ---
-import { AstroParameterConflictError } from '@/types/Errors';
-import type { HTMLAttributes, HTMLTag } from 'astro/types';
+import type { HTMLAttributes } from 'astro/types';
 
-interface Props extends HTMLAttributes<'button'> {
-  href?: string;
-  prefetch?: boolean;
-}
-const { href, onclick, prefetch, class: className, ...rest } = Astro.props;
+type Props = HTMLAttributes<'button'> | HTMLAttributes<'a'>;
+let { class: className, ...rest } = Astro.props;
 
-if (href && onclick) throw new AstroParameterConflictError('href', 'onclick');
+function isAnchor(props: Props): props is HTMLAttributes<'a'> {
+  return 'href' in props;
+}
 
-const Tag = (href ? 'a' : Fragment) as HTMLTag;
+const Tag = isAnchor(rest) ? 'a' : 'button';
 ---
 
-<Tag {...{ href, 'data-astro-prefetch': prefetch as boolean }}>
-  <button {...{ onclick, ...rest }} class:list={[className]}>
-    <slot />
-  </button>
+<Tag class:list={['btn', className]} {...rest as object}>
+  <slot />
 </Tag>
-
-<style lang="scss">
-  button {
-    display: flex;
-    align-items: center;
-    padding: 0.5rem;
-    margin: 0.5rem;
-    text-align: center;
-    transition-duration: 300ms;
-    border-radius: 9999px;
-    width: fit-content;
-    height: fit-content;
-    justify-content: center;
-
-    &:hover {
-      @apply bg-[var(--theme-color-light)] dark:bg-[var(--theme-color-dark)];
-    }
-
-    &:active {
-      @apply scale-95 brightness-75;
-    }
-  }
-</style>
src/components/widgets/DarkModeButton.astro
@@ -13,70 +13,84 @@ const { class: className, showText, ...rest } = Astro.props;
 ---
 
 <Button
-  class:list={['darkmode-btn', className]}
+  class:list={['darkmode-btn swap swap-rotate', className]}
   {...rest}
   data-text-light={i18n(I18nKey.lightMode)}
   data-text-dark={i18n(I18nKey.darkMode)}
   data-text-auto={i18n(I18nKey.systemMode)}
 >
-  <Icon class="darkmode-icon-light" name="material-symbols:light-mode-rounded" />
-  <Icon class="darkmode-icon-dark" name="material-symbols:dark-mode-rounded" />
-  <Icon class="darkmode-icon-auto" name="material-symbols:night-sight-auto-rounded" />
-  {showText && <span class="darkmode-text ml-auto mr-2 px-2" />}
+  <input type="checkbox" />
+  <Icon class="darkmode-icon-light swap-off" name="material-symbols:light-mode-rounded" />
+  <Icon class="darkmode-icon-dark swap-on" name="material-symbols:dark-mode-rounded" />
+  <Icon
+    class="darkmode-icon-auto swap-indeterminate"
+    name="material-symbols:night-sight-auto-rounded"
+  />
+  {showText && <span class="darkmode-text pl-6" />}
 </Button>
 
 <script>
   function initDarkmodeButtons() {
     const darkmodeBtns = document.querySelectorAll('button.darkmode-btn');
 
-    function refreshButtons() {
-      darkmodeBtns.forEach((btn) => {
-        const iconLight = btn.querySelector('.darkmode-icon-light');
-        const iconDark = btn.querySelector('.darkmode-icon-dark');
-        const iconAuto = btn.querySelector('.darkmode-icon-auto');
-        const text = btn.querySelector('.darkmode-text');
+    darkmodeBtns.forEach((btn) => {
+      const checkbox = btn.querySelector('input[type="checkbox"]') as HTMLInputElement;
+      const text = btn.querySelector('.darkmode-text');
+
+      // 初始化状态
+      if ('darkMode' in localStorage) {
+        checkbox.checked = localStorage.darkMode === 'true';
+        checkbox.indeterminate = false;
+      } else {
+        checkbox.indeterminate = true;
+      }
+
+      // 更新文本和标题
+      function updateText() {
+        if (!text) return;
 
-        if ('darkMode' in localStorage && localStorage.darkMode === 'true') {
-          iconLight?.classList.add('hidden');
-          iconDark?.classList.remove('hidden');
-          iconAuto?.classList.add('hidden');
-          if (text) text.textContent = btn.getAttribute('data-text-dark');
-          btn.setAttribute('title', btn.getAttribute('data-text-dark') || '');
-        } else if ('darkMode' in localStorage && localStorage.darkMode === 'false') {
-          iconLight?.classList.remove('hidden');
-          iconDark?.classList.add('hidden');
-          iconAuto?.classList.add('hidden');
-          if (text) text.textContent = btn.getAttribute('data-text-light');
-          btn.setAttribute('title', btn.getAttribute('data-text-light') || '');
+        const textContent = checkbox.indeterminate
+          ? btn.getAttribute('data-text-auto')
+          : checkbox.checked
+            ? btn.getAttribute('data-text-dark')
+            : btn.getAttribute('data-text-light');
+
+        text.textContent = textContent;
+        btn.setAttribute('title', textContent || '');
+      }
+
+      // 更新主题
+      function updateTheme() {
+        if (checkbox.indeterminate) {
+          localStorage.removeItem('darkMode');
+          const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+          document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
         } else {
-          iconLight?.classList.add('hidden');
-          iconDark?.classList.add('hidden');
-          iconAuto?.classList.remove('hidden');
-          if (text) text.textContent = btn.getAttribute('data-text-auto');
-          btn.setAttribute('title', btn.getAttribute('data-text-auto') || '');
+          localStorage.darkMode = checkbox.checked;
+          document.documentElement.setAttribute(
+            'data-theme',
+            checkbox.checked ? 'dark' : 'light'
+          );
         }
-      });
-    }
+      }
 
-    darkmodeBtns.forEach((btn) => {
       btn.addEventListener('click', () => {
-        if (!('darkMode' in localStorage)) {
-          localStorage.darkMode = false;
-          document.documentElement.classList.remove('dark');
-        } else if (localStorage.darkMode === 'false') {
-          localStorage.darkMode = true;
-          document.documentElement.classList.add('dark');
+        if (checkbox.indeterminate) {
+          checkbox.indeterminate = false;
+          checkbox.checked = false;
+        } else if (!checkbox.checked) {
+          checkbox.checked = true;
         } else {
-          localStorage.removeItem('darkMode');
-          if (window.matchMedia('(prefers-color-scheme: dark)').matches)
-            document.documentElement.classList.add('dark');
-          else document.documentElement.classList.remove('dark');
+          checkbox.indeterminate = true;
         }
-        refreshButtons();
+
+        updateText();
+        updateTheme();
       });
-    });
 
-    refreshButtons();
+      updateText();
+      updateTheme();
+    });
   }
 
   document.addEventListener('astro:page-load', initDarkmodeButtons);
src/components/widgets/MetaIcon.astro
@@ -11,15 +11,13 @@ const { class: className, ...rest } = Astro.props;
 
 <style lang="scss">
   .meta-icon {
-    width: 2rem;
-    height: 2rem;
+    width: 1.5rem;
+    height: 1.5rem;
     align-items: center;
     margin-right: 0.5rem;
     display: flex;
     justify-content: center;
 
-    @apply text-[var(--theme-color-light-darken)] dark:text-[var(--theme-color-dark-lighten)];
-    @apply bg-[var(--theme-color-light-trans-1d2)] dark:bg-[var(--theme-color-dark-trans-1d2)];
-    @apply rounded-md;
+    @apply text-primary;
   }
 </style>
src/components/widgets/Pagination.astro
@@ -46,36 +46,35 @@ else {
 <div id="pagination" class="flex w-full">
   {
     current > 1 && (
-      <Button
-        id="prev-page-btn"
-        class="theme-bg theme-border mr-auto !rounded-xl border-2"
-        href={getPageUrl(current - 1)}
-      >
+      <Button id="prev-page-btn" class="mr-auto rounded-xl" href={getPageUrl(current - 1)}>
         <Icon name="material-symbols:keyboard-double-arrow-left-rounded" class="my-1" />
       </Button>
     )
   }
   <div class="mx-auto flex items-center justify-center gap-2">
-    {
-      pages.map((p) => {
-        return (
-          <Button
-            class:list={[
-              'theme-bg theme-border !rounded-xl border-2',
-              current === p.page && 'theme-card-bg-hl',
-            ]}
-            href={p.url}
-          >
-            <span class="mx-1">{`${p.page === -1 ? '...' : p.page}`}</span>
-          </Button>
-        );
-      })
-    }
+    <div class="join rounded-xl">
+      {
+        pages.map((p) => {
+          return (
+            <Button
+              class:list={[
+                'join-item',
+                current === p.page && 'btn-active',
+                p.page === -1 && 'btn-disabled',
+              ]}
+              href={p.url}
+            >
+              <span class="mx-1">{`${p.page === -1 ? '...' : p.page}`}</span>
+            </Button>
+          );
+        })
+      }
+    </div>
     {
       total > 1 && (
-        <div
+        <label
           id="page-jumper"
-          class="theme-card-bg theme-border mx-2 flex flex-row items-center rounded-xl border-2"
+          class="input input-bordered mx-2 flex flex-row items-center overflow-hidden rounded-xl px-0"
           data-base-url={baseUrl}
           data-special-pages={specialPages?.map((p) => `${p.page}:${p.url}`).join(',')}
         >
@@ -84,25 +83,18 @@ else {
             type="number"
             min="1"
             max={total}
-            class="theme-bg !active:border-none border-none pl-4 duration-300"
+            class="pl-4 duration-300"
           />
-          <Button
-            id="page-jumper-button"
-            class="theme-card-bg relative right-0 !m-0 !rounded-xl duration-300"
-          >
+          <Button id="page-jumper-button" class="relative right-0 m-0 rounded-xl duration-300">
             <Icon name="material-symbols:keyboard-double-arrow-right-rounded" class="my-1" />
           </Button>
-        </div>
+        </label>
       )
     }
   </div>
   {
     current < total && (
-      <Button
-        id="next-page-btn"
-        class="theme-bg theme-border ml-auto !rounded-xl border-2"
-        href={getPageUrl(current + 1)}
-      >
+      <Button id="next-page-btn" class="ml-auto rounded-xl" href={getPageUrl(current + 1)}>
         <Icon name="material-symbols:keyboard-double-arrow-right-rounded" class="my-1" />
       </Button>
     )
src/components/widgets/PostCard.astro
@@ -48,14 +48,15 @@ const metas: ({ icon: string; text: string; link?: string } | undefined)[] = [
 
 <div
   class:list={[
-    'theme-card-bg theme-border',
-    'flex w-full rounded-xl border-2 p-4 max-md:flex-col-reverse',
+    'card md:card-side card-bordered flex w-full rounded-xl border-2 max-md:flex-col-reverse',
     className,
   ]}
 >
-  <div class="mr-auto h-full w-full items-center px-12 py-7">
-    <div class="mb-5 text-2xl"><a href={url}>{title}</a></div>
-    <div class="theme-text-second mb-3 flex flex-wrap items-center gap-x-4 gap-y-2">
+  <div class="card-body">
+    <a href={url} class="card-title">{title}</a>
+    <div
+      class="text-base-content mb-3 flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-opacity-60"
+    >
       {
         metas.map((meta) => {
           return (
@@ -78,19 +79,13 @@ const metas: ({ icon: string; text: string; link?: string } | undefined)[] = [
   </div>
   {
     hasCover ? (
-      <PostCardCover url={url} title={title} cover={cover} />
+      <figure class="md:w-3/4 md:max-w-96">
+        <PostCardCover url={url} title={title} cover={cover} />
+      </figure>
     ) : (
-      <ReadMoreButton href={url} title={title} />
+      <figure>
+        <ReadMoreButton href={url} title={title} />
+      </figure>
     )
   }
 </div>
-
-<style>
-  .meta-text {
-    @apply text-sm font-medium;
-  }
-
-  a.meta-text {
-    @apply duration-100 hover:brightness-125;
-  }
-</style>
src/components/widgets/PostCardCover.astro
@@ -20,14 +20,11 @@ const { url, title, cover } = Astro.props;
 
 <style>
   a {
-    @apply relative flex min-h-48 w-full overflow-hidden rounded-md md:w-3/4 md:max-w-96;
-    @apply duration-100 active:scale-95 active:brightness-75;
+    @apply relative flex min-h-48 w-full overflow-hidden duration-100 active:brightness-75;
   }
 
   div.cover-mask {
-    @apply absolute inset-0 z-10 h-full w-full bg-black/60;
-    @apply flex items-center justify-center;
-    @apply opacity-0 duration-300 group-hover:opacity-100;
+    @apply absolute inset-0 z-10 flex h-full w-full items-center justify-center bg-black/60 opacity-0 duration-300 group-hover:opacity-100;
   }
 
   svg {
src/components/widgets/ProfileCard.astro
@@ -5,33 +5,25 @@ import { Icon } from 'astro-icon/components';
 import Button from './Button.astro';
 ---
 
-<div
-  id="profile-card"
-  transition:persist
-  class="theme-card-bg theme-border flex flex-col items-center rounded-xl border-2 text-center"
->
-  <div class="m-3 w-fit min-w-20">
+<div id="profile-card" transition:persist class="card card-bordered border-2">
+  <figure class="px-4 pt-4">
     <a href="/about/">
-      <ImageWrapper
-        class="theme-border h-20 w-20 rounded-full border-4"
-        src={profileConfig.avatar}
-        alt={profileConfig.name}
-      />
+      <ImageWrapper class="rounded-xl" src={profileConfig.avatar} alt={profileConfig.name} />
     </a>
-  </div>
-  <div class="mx-3 flex w-full flex-col">
-    <div class="mb-3 text-lg">
+  </figure>
+  <div class="card-body items-center p-5 text-center">
+    <div class="card-title">
       <a href="/about/" class="font-bold">{profileConfig.name}</a>
     </div>
     <div>{profileConfig.bio}</div>
-  </div>
-  <div class="flex items-center justify-center">
-    {
-      profileConfig.links.map((link) => (
-        <Button href={link.url} title={link.name} class="text-2xl">
-          <Icon name={link.icon} />
-        </Button>
-      ))
-    }
+    <div class="flex flex-row flex-wrap items-center justify-center gap-2">
+      {
+        profileConfig.links.map((link) => (
+          <Button href={link.url} title={link.name} class="btn-circle btn-ghost text-2xl">
+            <Icon name={link.icon} />
+          </Button>
+        ))
+      }
+    </div>
   </div>
 </div>
src/components/widgets/ReadMoreButton.astro
@@ -15,14 +15,10 @@ const { href, title, ...rest } = Astro.props;
 
 <style>
   a {
-    @apply duration-100 max-md:hidden;
-    @apply hover:brightness-125;
-    @apply active:scale-95 active:brightness-75;
+    @apply duration-150 hover:brightness-125 active:brightness-75 max-md:hidden;
   }
 
   svg {
-    @apply h-full min-h-48 w-12 rounded-md;
-    @apply text-[var(--theme-color-light-darken)] dark:text-[var(--theme-color-dark-lighten)];
-    @apply bg-[var(--theme-color-light-trans-1d2)] dark:bg-[var(--theme-color-dark-trans-1d2)];
+    @apply text-primary bg-primary/40 h-full min-h-48 w-12;
   }
 </style>
src/components/widgets/TOC.astro
@@ -11,17 +11,18 @@ const minDepth = Math.min(...headings.map((h) => h.depth));
 const maxLevel = 3;
 ---
 
-<div id="toc" class:list={['theme-card-bg theme-border rounded-xl border-2 p-2', className]}>
-  <div></div>
-  {
-    headings
-      .filter((heading) => heading.depth < minDepth + maxLevel)
-      .map((heading) => (
-        <a class:list={[`level-${heading.depth - minDepth + 1}`]} href={`#${heading.slug}`}>
-          {heading.text}
-        </a>
-      ))
-  }
+<div id="toc" class:list={['card card-bordered border-2', className]}>
+  <div class="card-body p-2">
+    {
+      headings
+        .filter((heading) => heading.depth < minDepth + maxLevel)
+        .map((heading) => (
+          <a class:list={[`level-${heading.depth - minDepth + 1}`]} href={`#${heading.slug}`}>
+            {heading.text}
+          </a>
+        ))
+    }
+  </div>
 </div>
 
 <style lang="scss">
src/components/CategoryBar.astro
@@ -10,31 +10,26 @@ interface Props {
 const { categories, currentCategory } = Astro.props;
 ---
 
-<div
-  id="category-bar"
-  class="theme-card-bg theme-border mb-4 flex w-full rounded-xl border-2 px-2 py-3"
->
-  <a href={`/`} class:list={[currentCategory ? '' : 'theme-card-bg-hl']}>
-    {i18n(I18nKey.recentPosts)}
-  </a>
-  {
-    categories.map((category) => (
-      <a
-        href={`/archives/categories/${category}/1/`}
-        class:list={[currentCategory === category ? 'theme-card-bg-hl' : '']}
-      >
-        {i18n(category)}
-      </a>
-    ))
-  }
-</div>
-
-<style lang="scss">
-  a {
-    @apply mx-2 rounded-md px-2 py-1;
-
-    &:hover {
-      @apply bg-[var(--theme-color-light-lighten)] dark:bg-[var(--theme-color-dark-lighten)];
+<div id="category-bar" class="card card-bordered mb-4 w-full border-2">
+  <div class="card-body flex flex-row items-center gap-2 overflow-auto px-2 py-3">
+    <a
+      href={`/`}
+      class:list={['btn btn-ghost h-8 min-h-8 px-3 py-0', currentCategory ? '' : 'btn-active']}
+    >
+      {i18n(I18nKey.recentPosts)}
+    </a>
+    {
+      categories.map((category) => (
+        <a
+          href={`/archives/categories/${category}/1/`}
+          class:list={[
+            'btn btn-ghost h-8 min-h-8 px-3 py-0',
+            currentCategory === category ? 'btn-active' : '',
+          ]}
+        >
+          {i18n(category)}
+        </a>
+      ))
     }
-  }
-</style>
+  </div>
+</div>
src/components/License.astro
@@ -29,8 +29,8 @@ const infomations = [
 ];
 ---
 
-<div class="theme-card-bg-hl-trans theme-border my-4 flex items-center rounded-xl border-2 p-4">
-  <div class="mx-auto grid grid-cols-2 gap-x-4">
+<div class="card card-bordered bg-primary/25 my-4 border-2">
+  <div class="card-body grid grid-cols-2 gap-x-4 p-4">
     {
       infomations.map((info) => (
         <Fragment>
src/components/Navbar.astro
@@ -3,6 +3,7 @@ import { navbarConfig } from '@/config';
 import { i18n } from '@i18n/translation';
 import { Icon } from 'astro-icon/components';
 import Button from './widgets/Button.astro';
+import DarkModeButton from './widgets/DarkModeButton.astro';
 
 interface Props {
   title?: string;
@@ -12,77 +13,124 @@ let { title } = Astro.props;
 if (!title) title = 'Astral Halo';
 ---
 
-<div
-  id="navbar"
-  class="theme-border theme-card-bg fixed z-30 flex h-16 w-full items-center border-b-2"
->
-  <div id="nav-left" class="mr-auto flex w-fit">
-    <Button id="site-name" href="/" class="group">
-      <span class="text-xl font-bold duration-300 group-hover:opacity-0">{title}</span>
-      <Icon
-        name="material-symbols:home-rounded"
-        class="absolute text-3xl opacity-0 duration-300 group-hover:opacity-100"
-      />
-    </Button>
-  </div>
-  <div id="nav-center" class="m-auto flex w-fit max-md:hidden">
-    {
-      navbarConfig.navbarCenterItems.map((item) => (
-        <Button
-          {...('href' in item && item.href && { href: item.href })}
-          title={i18n(item.text)}
-          class="!px-4"
-        >
-          <span class="text-xl tracking-wide">{i18n(item.text)}</span>
+<div class="drawer drawer-end">
+  <input id="sidebar-drawer" type="checkbox" class="drawer-toggle" />
+  <div class="drawer-content flex flex-col">
+    <!-- Navbar -->
+    <div
+      id="navbar"
+      class="navbar bg-base-200/50 fixed z-20 flex h-16 w-full items-center backdrop-blur-md"
+    >
+      <div class="navbar-start">
+        <Button id="site-name" href="/" class="btn-ghost group">
+          <span class="text-xl font-bold duration-300 group-hover:opacity-0">{title}</span>
+          <Icon
+            name="material-symbols:home-rounded"
+            class="absolute text-3xl opacity-0 duration-300 group-hover:opacity-100"
+          />
         </Button>
-      ))
-    }
+      </div>
+      <nav class="navbar-center join max-md:hidden">
+        {
+          navbarConfig.navbarCenterItems.map((item) => (
+            <Button
+              {...('href' in item && item.href && { href: item.href })}
+              title={i18n(item.text)}
+              class="btn-ghost join-item"
+            >
+              <span class="text-xl tracking-wide">{i18n(item.text)}</span>
+            </Button>
+          ))
+        }
+      </nav>
+      <div class="navbar-end">
+        <div class="flex max-md:hidden">
+          {
+            navbarConfig.navbarRightItems.onlyWide.map((item) => (
+              <Button
+                class="nav-menu-item btn-ghost btn-circle"
+                {...('href' in item && item.href && { href: item.href })}
+                title={i18n(item.text)}
+                {...('onclick' in item &&
+                  item.onclick &&
+                  (typeof item.onclick === 'string'
+                    ? { onclick: item.onclick }
+                    : { id: 'nav-' + item.onclick.id }))}
+              >
+                <Icon name={item.icon} class="text-2xl" />
+              </Button>
+            ))
+          }
+        </div>
+        <div class="flex">
+          {
+            navbarConfig.navbarRightItems.always.map((item) => (
+              <Button
+                class="nav-menu-item btn-ghost btn-circle"
+                {...('href' in item && item.href && { href: item.href })}
+                title={i18n(item.text)}
+                {...('onclick' in item &&
+                  item.onclick &&
+                  (typeof item.onclick === 'string'
+                    ? { onclick: item.onclick }
+                    : { id: 'nav-' + item.onclick.id }))}
+              >
+                <Icon name={item.icon} class="text-2xl" />
+              </Button>
+            ))
+          }
+        </div>
+        <div class="md:hidden">
+          <label for="sidebar-drawer" class="btn btn-ghost btn-circle">
+            <Icon name="material-symbols:menu-rounded" class="text-2xl" />
+          </label>
+        </div>
+      </div>
+    </div>
+    <div id="navbar-placeholder" class="pt-20"></div>
+    <!-- Page Content -->
+    <slot />
   </div>
-  <div id="nav-right" class="ml-auto flex w-fit">
-    <div class="flex max-md:hidden">
+  <div class="drawer-side z-50">
+    <!-- Sidebar -->
+    <label for="sidebar-drawer" class="drawer-overlay"></label>
+    <ul class="menu bg-base-200 min-h-full w-[min(calc(100%-3rem),20rem)] p-4">
+      <li><DarkModeButton class="btn-ghost text-xl" showText={true} /></li>
       {
-        navbarConfig.navbarRightItems.onlyWide.map((item) => (
-          <Button
-            class="nav-menu-item"
-            {...('href' in item && item.href && { href: item.href })}
-            title={i18n(item.text)}
-            {...('onclick' in item &&
-              item.onclick &&
-              (typeof item.onclick === 'string'
-                ? { onclick: item.onclick }
-                : { id: 'nav-' + item.onclick.id }))}
-          >
-            <Icon name={item.icon} class="text-2xl" />
-          </Button>
+        navbarConfig.navbarCenterItems.map((item) => (
+          <li>
+            <Button
+              {...('href' in item && item.href && { href: item.href })}
+              title={i18n(item.text)}
+              class="btn-ghost"
+            >
+              <span class="text-xl">{i18n(item.text)}</span>
+            </Button>
+          </li>
         ))
       }
-    </div>
-    <div class="flex">
       {
-        navbarConfig.navbarRightItems.always.map((item) => (
-          <Button
-            class="nav-menu-item"
-            {...('href' in item && item.href && { href: item.href })}
-            title={i18n(item.text)}
-            {...('onclick' in item &&
-              item.onclick &&
-              (typeof item.onclick === 'string'
-                ? { onclick: item.onclick }
-                : { id: 'nav-' + item.onclick.id }))}
-          >
-            <Icon name={item.icon} class="text-2xl" />
-          </Button>
+        navbarConfig.navbarRightItems.onlyWide.map((item) => (
+          <li>
+            <Button
+              {...('href' in item && item.href && { href: item.href })}
+              title={i18n(item.text)}
+              {...('onclick' in item &&
+                item.onclick &&
+                (typeof item.onclick === 'string'
+                  ? { onclick: item.onclick }
+                  : { id: 'side-' + item.onclick.id }))}
+              class="btn-ghost"
+            >
+              <Icon name={item.icon} class="text-2xl" />
+              <span class="text-xl">{i18n(item.text)}</span>
+            </Button>
+          </li>
         ))
       }
-    </div>
-    <div class="flex md:hidden">
-      <Button id="nav-toggle-sidebar-btn">
-        <Icon name="material-symbols:menu-rounded" class="text-2xl" />
-      </Button>
-    </div>
+    </ul>
   </div>
 </div>
-<div id="navbar-placeholder" class="pt-20"></div>
 
 <script>
   import { navbarConfig } from '@/config';
src/components/PageFooter.astro
@@ -4,39 +4,36 @@ import { footerConfig, profileConfig } from '@/config';
 const currentYear = new Date().getFullYear();
 ---
 
-<footer id="footer" class="relative mt-auto w-full flex-shrink-0">
-  <div id="footer-links"></div>
-  <div
-    id="footer-bar-wrapper"
-    class="theme-card-bg theme-border relative bottom-0 mt-4 max-h-fit w-full overflow-hidden border-t-2 p-4"
-  >
-    <div id="footer-bar" class="flex justify-between gap-6">
-      <div id="footer-left" class="text-center">
-        © {footerConfig.copyrightYear}{
-          footerConfig.copyrightYear < currentYear && `-${currentYear}`
-        }
-        <a href="/about/">{profileConfig.name}</a>
-      </div>
-      <div id="footer-right" class="flex flex-wrap justify-items-end gap-4">
-        {
-          footerConfig.rightItems.map((item) => (
-            <span>
-              {item.map((c) => {
-                if (typeof c === 'string') return <span>{c}</span>;
-                else if ('link' in c)
-                  return (
-                    <a href={c.link} class={c.class || ''}>
-                      {c.text}
-                    </a>
-                  );
-                else return <span class:list={c.class || ''}>{c.text}</span>;
-              })}
-            </span>
-          ))
-        }
-      </div>
-    </div>
-  </div>
+<footer class="footer mt-auto flex-shrink-0"></footer>
+<footer
+  class="footer bg-base-200 text-base-content border-base-300 border-t px-10 py-4 text-base"
+>
+  <aside class="items-center">
+    <p>
+      © {footerConfig.copyrightYear}{
+        footerConfig.copyrightYear < currentYear && `-${currentYear}`
+      }
+      <a href="/about/" class="font-bold">{profileConfig.name}</a>
+    </p>
+  </aside>
+  <nav class="flex flex-row md:place-self-center md:justify-self-end">
+    {
+      footerConfig.rightItems.map((item) => (
+        <span>
+          {item.map((c) => {
+            if (typeof c === 'string') return <span>{c}</span>;
+            else if ('link' in c)
+              return (
+                <a href={c.link} class={c.class || ''}>
+                  {c.text}
+                </a>
+              );
+            else return <span class:list={c.class || ''}>{c.text}</span>;
+          })}
+        </span>
+      ))
+    }
+  </nav>
 </footer>
 
 <style lang="scss">
@@ -44,7 +41,7 @@ const currentYear = new Date().getFullYear();
     @apply duration-150;
 
     &:hover {
-      @apply text-[var(--theme-color-light)] dark:text-[var(--theme-color-dark)];
+      @apply text-primary;
     }
   }
 </style>
src/components/Search.astro
@@ -4,14 +4,11 @@ import Algolia from './search/Algolia.astro';
 import Pagefind from './search/Pagefind.astro';
 ---
 
-<div id="search-container" class="fixed z-30 hidden h-full w-full" transition:persist>
-  <div
-    class="fixed -z-10 h-full w-full bg-black/20 backdrop-blur-sm backdrop-saturate-100 duration-500 ease-in-out"
-  >
-  </div>
-  <div
-    class="theme-card-bg theme-border mx-auto mt-[min(10%,8rem)] flex w-11/12 flex-col items-center justify-center gap-4 overflow-hidden rounded-xl border-2 md:w-1/2"
-  >
+<dialog id="search_modal" class="modal" transition:persist>
+  <div class="modal-box bg-base-300 border-base-200">
+    <form method="dialog">
+      <button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
+    </form>
     <div class="w-full p-4">
       {
         (() => {
@@ -24,32 +21,21 @@ import Pagefind from './search/Pagefind.astro';
         })()
       }
     </div>
-    <div
-      class="theme-card-bg-hl-trans relative mt-auto w-full flex-shrink-0 px-4 py-2 text-center"
-    >
+    <div class="relative mt-auto w-full flex-shrink-0 pt-4 text-center">
       Powered by {
         searchConfig.provider === 'pagefind' ? (
-          <a href="https://pagefind.app" target="_blank" class="text-blue-500">
+          <a href="https://pagefind.app" target="_blank" class="text-primary">
             Pagefind
           </a>
         ) : (
-          <a href="https://www.algolia.com" target="_blank" class="text-blue-500">
+          <a href="https://www.algolia.com" target="_blank" class="text-primary">
             Algolia
           </a>
         )
       }
     </div>
   </div>
-</div>
-
-<script>
-  function initSearchContainer() {
-    const searchContainer = document.getElementById('search-container');
-    const searchContainerMask = searchContainer?.querySelector(':first-child');
-    searchContainerMask?.addEventListener('click', () => {
-      searchContainer?.classList.add('hidden');
-    });
-  }
-
-  document.addEventListener('astro:page-load', initSearchContainer);
-</script>
+  <form method="dialog" class="modal-backdrop">
+    <button>close</button>
+  </form>
+</dialog>
src/components/Sidebar.astro
@@ -1,100 +0,0 @@
----
-import { navbarConfig } from '@/config';
-import { i18n } from '@i18n/translation';
-import { Icon } from 'astro-icon/components';
-import Button from './widgets/Button.astro';
-import DarkModeButton from './widgets/DarkModeButton.astro';
----
-
-<div id="sidebar">
-  <div
-    id="sidebar-mask"
-    class="pointer-events-none fixed z-40 h-full w-full bg-black/10 opacity-0 backdrop-blur-md backdrop-saturate-100 duration-500 ease-in-out"
-  >
-  </div>
-  <div
-    id="sidebar-menu"
-    class="theme-border theme-card-bg fixed -right-1/2 z-50 h-full w-1/2 border-l-2 duration-500 ease-in-out"
-  >
-    <div id="sidebar-site-data"></div>
-    <DarkModeButton
-      showText={true}
-      class="theme-border !w-[calc(100%-1rem)] border-2 text-lg"
-    />
-    <div id="sidebar-menu-text-items">
-      {
-        navbarConfig.navbarCenterItems.map((item) => (
-          <Button
-            id="sidebar-menu-item"
-            {...('href' in item && item.href && { href: item.href })}
-            title={i18n(item.text)}
-          >
-            <span class="text-xl">{i18n(item.text)}</span>
-          </Button>
-        ))
-      }
-    </div>
-    <div id="sidebar-menu-icon-items" class="flex">
-      {
-        navbarConfig.navbarRightItems.onlyWide.map((item) => (
-          <Button
-            id="sidebar-menu-item"
-            {...('href' in item && item.href && { href: item.href })}
-            title={i18n(item.text)}
-            {...('onclick' in item &&
-              item.onclick &&
-              (typeof item.onclick === 'string'
-                ? { onclick: item.onclick }
-                : { id: 'side-' + item.onclick.id }))}
-          >
-            <Icon name={item.icon} class="text-2xl" />
-          </Button>
-        ))
-      }
-    </div>
-  </div>
-</div>
-
-<script>
-  import { navbarConfig } from '@/config';
-  const rightItems = navbarConfig.navbarRightItems.onlyWide;
-
-  function initSidebar() {
-    const toggleSidebarBtns = document.getElementById('nav-toggle-sidebar-btn');
-    const sidebarMask = document.getElementById('sidebar-mask');
-    const sidebarMenu = document.getElementById('sidebar-menu');
-
-    toggleSidebarBtns?.addEventListener('click', () => {
-      sidebarMask?.classList.remove('opacity-0');
-      sidebarMask?.classList.remove('pointer-events-none');
-      sidebarMenu?.classList.add('-translate-x-full');
-      sidebarMenu?.removeAttribute('inert');
-    });
-
-    sidebarMask?.addEventListener('click', () => {
-      sidebarMask?.classList.add('opacity-0');
-      sidebarMask?.classList.add('pointer-events-none');
-      sidebarMenu?.classList.remove('-translate-x-full');
-      sidebarMenu?.setAttribute('inert', 'true');
-    });
-
-    window.addEventListener('resize', () => {
-      if (window.innerWidth > 768) {
-        sidebarMask?.classList.add('opacity-0');
-        sidebarMask?.classList.add('pointer-events-none');
-        sidebarMenu?.classList.remove('-translate-x-full');
-        sidebarMenu?.setAttribute('inert', 'true');
-      }
-    });
-
-    // 绑定设置的点击事件
-    rightItems.forEach((item) => {
-      if ('onclick' in item && item.onclick && typeof item.onclick !== 'string') {
-        const element = document.getElementById('side-' + item.onclick.id);
-        if (element) element.addEventListener('click', item.onclick.function);
-      }
-    });
-  }
-
-  document.addEventListener('astro:page-load', initSidebar);
-</script>
src/components/SideToolBar.astro
@@ -5,23 +5,23 @@ import Button from './widgets/Button.astro';
 import DarkModeButton from './widgets/DarkModeButton.astro';
 ---
 
-<div id="side-toolbar" class="fixed bottom-10 right-0 z-30">
-  <div id="stb-hide" class="translate-x-full duration-500 ease-in-out" inert>
-    <DarkModeButton id="stb-dark-mode" class="" />
+<div id="side-toolbar" class="fixed bottom-10 right-0 z-30 grid grid-cols-1 gap-2">
+  <div id="stb-hide" class="translate-x-full duration-500 ease-in-out grid grid-cols-1 gap-2 pr-4" inert>
+    <DarkModeButton id="stb-dark-mode" class="btn-circle btn-primary btn-sm" />
   </div>
-  <div id="stb-show" class="translate-x-full duration-500 ease-in-out" inert>
-    <Button id="stb-show-more">
+  <div id="stb-show" class="translate-x-full duration-500 ease-in-out grid grid-cols-1 gap-2 pr-4" inert>
+    <Button id="stb-show-more" class='btn-circle btn-primary btn-sm'>
       <Icon name="material-symbols:settings-rounded" class="animate-spin" />
     </Button>
     {
       articleConfig.toc && (
-        <Button id="stb-toc" class="hidden xl:!hidden">
+        <Button id="stb-toc" class="hidden xl:!hidden btn-circle btn-primary btn-sm">
           <Icon name="material-symbols:toc-rounded" />
         </Button>
         <div id="stb-toc-wrapper" class="absolute scale-0 hidden duration-300 bottom-10 max-w-72 w-[calc(100vw-4rem)]"></div>
       )
     }
-    <Button id="stb-back-to-top" class="group">
+    <Button id="stb-back-to-top" class='group btn-circle btn-primary btn-sm'>
       <span
         id="stb-read-percentage"
         class="absolute text-sm opacity-0 duration-300 group-hover:opacity-0"></span>
@@ -113,12 +113,3 @@ import DarkModeButton from './widgets/DarkModeButton.astro';
   document.addEventListener('astro:page-load', initSideToolBar);
 </script>
 
-<style lang="scss">
-  button {
-    @apply bg-[var(--theme-color-light)] dark:bg-[var(--theme-color-dark)];
-
-    &:hover {
-      @apply bg-[var(--theme-color-light-lighten)] dark:bg-[var(--theme-color-dark-lighten)];
-    }
-  }
-</style>
src/layouts/GlobalLayout.astro
@@ -46,7 +46,7 @@ const siteLang = lang.replace('_', '-');
 
     <slot name="head" />
   </head>
-  <body class="theme-bg theme-text flex min-h-screen flex-col">
+  <body class="bg-base-100 text-base-content flex min-h-screen flex-col">
     <slot />
   </body>
 </html>
@@ -59,8 +59,8 @@ const siteLang = lang.replace('_', '-');
       (!('darkMode' in localStorage) &&
         window.matchMedia('(prefers-color-scheme: dark)').matches)
     ) {
-      document.documentElement.classList.add('dark');
-    } else document.documentElement.classList.remove('dark');
+      document.documentElement.setAttribute('data-theme', 'dark');
+    } else document.documentElement.setAttribute('data-theme', 'light');
   }
   document.addEventListener('astro:after-swap', applyDarkMode);
   applyDarkMode();
src/layouts/MainLayout.astro
@@ -4,7 +4,6 @@ import Navbar from '@components/Navbar.astro';
 import PageFooter from '@components/PageFooter.astro';
 import Search from '@components/Search.astro';
 import SideToolBar from '@components/SideToolBar.astro';
-import Sidebar from '@components/Sidebar.astro';
 import GlobalLayout from './GlobalLayout.astro';
 
 interface Props {
@@ -17,14 +16,14 @@ const { title, description, lang } = Astro.props;
 
 <GlobalLayout title={title} description={description} lang={lang}>
   <slot slot="head" name="head" />
-  <Sidebar />
   <SideToolBar />
-  <Navbar />
-  <slot name="header" />
-  <div id="body-wrap" class="w-full items-center md:px-4">
-    <!-- Main content -->
-    <slot />
-  </div>
+  <Navbar>
+    <slot name="header" />
+    <div id="body-wrap" class="w-full items-center md:px-4">
+      <!-- Main content -->
+      <slot />
+    </div>
+  </Navbar>
   {searchConfig.enable && <Search />}
   <PageFooter />
 </GlobalLayout>
src/pages/posts/[article].astro
@@ -35,7 +35,7 @@ const wordCount = countWords(article.body || '');
       class="mx-2 mt-4"
     />
   </Fragment>
-  <div class="theme-card-bg theme-border rounded-xl border-2 px-6 py-4">
+  <div class="card card-bordered rounded-xl border-2 px-6 py-4">
     <Markdown>
       <Content />
     </Markdown>
src/pages/about.astro
@@ -13,32 +13,34 @@ const aboutMd = await getEntry('spec', 'about');
 const { Content } = aboutMd ? await render(aboutMd) : Fragment;
 ---
 
-<MainLayout title={i18n(I18nKey.about)} comment={aboutMd?.data.comment}>
+<MainLayout title={i18n(I18nKey.about)}>
   <div class="mx-auto flex max-w-screen-xl flex-col items-center justify-center">
-    <div class="my-4 flex w-5/6 flex-row items-center justify-between gap-4 md:w-2/3">
-      <div class="mb-5 mr-auto flex flex-col">
-        <div>
-          <span class="text-xl">Hi! I'm </span>
-          <span class="text-2xl font-bold">{profileConfig.name}</span>
-        </div>
-        <div class="mt-2">
-          {profileConfig.bio}
-        </div>
-        <div class="mt-4 flex">
-          {
-            profileConfig.links.map((link) => (
-              <Button href={link.url} title={link.name} class="text-2xl">
-                <Icon name={link.icon} />
-              </Button>
-            ))
-          }
+    <div class="hero my-4 w-5/6 md:w-2/3">
+      <div class="hero-content flex-col-reverse md:flex-row">
+        <div class="mr-auto flex flex-col">
+          <h1>
+            <span class="text-xl">Hi! I'm </span>
+            <span class="text-2xl font-bold">{profileConfig.name}</span>
+          </h1>
+          <div class="mt-2">
+            {profileConfig.bio}
+          </div>
+          <div class="mt-4 flex">
+            {
+              profileConfig.links.map((link) => (
+                <Button href={link.url} title={link.name} class="btn-circle btn-ghost text-2xl">
+                  <Icon name={link.icon} />
+                </Button>
+              ))
+            }
+          </div>
         </div>
+        <ImageWrapper
+          class="h-0 min-h-48 w-0 min-w-48 rounded-lg shadow-2xl"
+          src={profileConfig.avatar}
+          alt={profileConfig.name}
+        />
       </div>
-      <ImageWrapper
-        class="theme-border h-0 min-h-32 w-0 min-w-32 rounded-full border-4 md:min-h-48 md:min-w-48"
-        src={profileConfig.avatar}
-        alt={profileConfig.name}
-      />
     </div>
     <Markdown class="w-5/6 md:w-3/4">
       <Content />
src/scripts/utils.ts
@@ -36,8 +36,3 @@ export async function getRandomPost() {
     console.error('Failed to get random post:', error);
   }
 }
-
-export function toggleSearch() {
-  const searchContainer = document.getElementById('search-container');
-  searchContainer?.classList.toggle('hidden');
-}
src/styles/globals.scss
@@ -1,106 +1,11 @@
-@use 'sass:color';
-@use './variables' as var;
 @tailwind base;
 @tailwind components;
 @tailwind utilities;
 
 :root {
-  --theme-color-light: #{var.$theme-color-light};
-  --theme-color-dark: #{var.$theme-color-dark};
-  --theme-color-light-lighten: #{color.adjust(var.$theme-color-light, $lightness: 20%)};
-  --theme-color-dark-lighten: #{color.adjust(var.$theme-color-dark, $lightness: 20%)};
-  --theme-color-light-darken: #{color.adjust(var.$theme-color-light, $lightness: -20%)};
-  --theme-color-dark-darken: #{color.adjust(var.$theme-color-dark, $lightness: -20%)};
-
-  @for $i from 1 through 8 {
-    $numerator: $i;
-    $denominator: 8;
-
-    @while $numerator % 2 == 0 and $denominator % 2 == 0 {
-      $numerator: calc($numerator / 2);
-      $denominator: calc($denominator / 2);
-    }
-
-    $alpha: calc($i / 8 - 1);
-
-    --theme-color-light-trans-#{$numerator}d#{$denominator}: #{color.adjust(
-        var.$theme-color-light,
-        $alpha: $alpha
-      )};
-    --theme-color-dark-trans-#{$numerator}d#{$denominator}: #{color.adjust(
-        var.$theme-color-dark,
-        $alpha: $alpha
-      )};
-  }
-
-  --theme-bg-color-light: #{var.$theme-bg-color-light};
-  --theme-bg-color-dark: #{var.$theme-bg-color-dark};
-  --theme-card-bg-color-light: #{var.$theme-card-bg-color-light};
-  --theme-card-bg-color-dark: #{var.$theme-card-bg-color-dark};
-  --theme-text-color-light: #{var.$theme-text-color-light};
-  --theme-text-color-dark: #{var.$theme-text-color-dark};
-  --theme-text-color-second-light: #{var.$theme-text-color-secondary-light};
-  --theme-text-color-second-dark: #{var.$theme-text-color-secondary-dark};
-  --theme-border-color-light: #{var.$theme-border-color-light};
-  --theme-border-color-dark: #{var.$theme-border-color-dark};
-
   font-size: 17px;
 }
 
 html {
   scroll-behavior: smooth;
 }
-
-@layer components {
-  .theme-bg {
-    @apply bg-[var(--theme-bg-color-light)] dark:bg-[var(--theme-bg-color-dark)];
-  }
-
-  .theme-card-bg {
-    @apply bg-[var(--theme-card-bg-color-light)] dark:bg-[var(--theme-card-bg-color-dark)];
-  }
-
-  .theme-card-bg-hl {
-    @apply bg-[var(--theme-color-light)] dark:bg-[var(--theme-color-dark)];
-  }
-
-  .theme-card-bg-hl-trans {
-    @apply bg-[var(--theme-color-light-trans-1d2)] dark:bg-[var(--theme-color-dark-trans-1d2)];
-  }
-
-  .theme-card-bg-contrary {
-    @apply bg-[var(--theme-bg-color-dark)] dark:bg-[var(--theme-bg-color-light)];
-  }
-
-  .theme-card-bg-contrary-hl {
-    @apply bg-[var(--theme-color-dark)] dark:bg-[var(--theme-color-light)];
-  }
-
-  .theme-text {
-    @apply text-[var(--theme-text-color-light)] dark:text-[var(--theme-text-color-dark)];
-  }
-
-  .theme-text-hl {
-    @apply text-[var(--theme-color-light)] dark:text-[var(--theme-color-dark)];
-  }
-
-  .theme-text-hl-contrast {
-    @apply text-[var(--theme-color-light-darken)] dark:text-[var(--theme-color-dark-lighten)];
-  }
-
-  .theme-text-second {
-    @apply text-[var(--theme-text-color-second-light)] dark:text-[var(--theme-text-color-second-dark)];
-  }
-
-  .theme-text-contrary {
-    @apply text-[var(--theme-text-color-dark)] dark:text-[var(--theme-text-color-light)];
-  }
-
-  .theme-border {
-    @apply border-[var(--theme-border-color-light)] dark:border-[var(--theme-border-color-dark)];
-  }
-
-  .theme-border-hl {
-    @apply border-[var(--theme-color-light)] dark:border-[var(--theme-color-dark)];
-  }
-}
src/styles/markdown.scss
@@ -38,7 +38,7 @@ article {
   }
 
   a {
-    @apply theme-text-hl-contrast underline decoration-dashed;
+    @apply text-primary underline decoration-dashed;
 
     &[data-footnote-ref],
     &[data-footnote-backref] {
@@ -81,8 +81,7 @@ article {
       border: 0.25em solid;
       border-radius: 9999px;
 
-      @apply border-[var(--theme-color-light-trans-3d4)];
-      @apply dark:border-[var(--theme-color-dark-trans-3d4)];
+      @apply border-primary/75;
     }
   }
 
@@ -106,7 +105,7 @@ article {
         text-align: center;
         border-radius: 9999px;
 
-        @apply theme-card-bg-hl-trans theme-text-hl-contrast;
+        @apply bg-primary/50 text-primary;
       }
 
       p:first-child {
@@ -152,22 +151,22 @@ article {
   code:not(pre code) {
     padding: 0 0.25rem;
 
-    @apply theme-text-hl-contrast rounded-md bg-[var(--theme-color-light-trans-1d8)] dark:bg-[var(--theme-color-dark-trans-1d8)];
+    @apply text-primary bg-primary/10 rounded-md;
   }
 
   // 引用块样式
   blockquote {
     padding: 0.25rem 0.25rem 0.25rem 0.75rem;
 
-    @apply theme-border-hl rounded-sm border-l-4 bg-[var(--theme-color-light-trans-1d8)] dark:bg-[var(--theme-color-dark-trans-1d8)];
+    @apply border-primary bg-primary/10 rounded-sm border-l-4;
   }
 
   // 折叠块样式
   details {
-    @apply theme-border w-full overflow-hidden rounded-xl border-2 duration-300;
+    @apply border-base-300 w-full overflow-hidden rounded-xl border-2 duration-300;
 
     summary {
-      @apply hover:theme-card-bg-hl-trans w-full px-3 py-1 text-start text-2xl duration-300;
+      @apply hover:bg-primary/50 w-full px-3 py-1 text-start text-2xl duration-300;
 
       &::marker {
         margin-right: 1.5rem;
@@ -176,7 +175,7 @@ article {
 
     &[open] {
       summary {
-        @apply theme-card-bg-hl-trans;
+        @apply bg-primary/50;
       }
     }
   }
src/styles/twikoo.scss
@@ -33,10 +33,10 @@
 
     // 文本框输入样式
     .el-textarea {
-      @apply theme-border relative w-full overflow-hidden rounded-xl border-2 pb-9;
+      @apply border-base-300 relative w-full overflow-hidden rounded-xl border-2 pb-9;
 
       textarea {
-        @apply theme-card-bg w-full p-4;
+        @apply bg-base-200/50 w-full p-4;
 
         resize: none;
 
@@ -45,12 +45,12 @@
         }
 
         &::placeholder {
-          @apply theme-text-second;
+          @apply text-base-content/50;
         }
       }
 
       .el-input__count {
-        @apply theme-text-second absolute bottom-2 right-10 text-sm;
+        @apply text-base-content/50 absolute bottom-2 right-10 text-sm;
       }
     }
 
@@ -71,7 +71,7 @@
       }
 
       button {
-        @apply theme-border theme-card-bg-hl absolute py-[0.95rem] md:py-[0.3rem];
+        @apply border-base-300 bg-primary absolute py-[0.95rem] md:py-[0.3rem];
 
         &.tk-preview {
           @apply bottom-[3.7rem] right-0 md:bottom-0 md:right-[3.35rem];
@@ -100,20 +100,20 @@
         }
 
         &.tk-cancel {
-          @apply theme-card-bg-contrary-hl theme-text-contrary bottom-[2.4rem] right-0 md:bottom-0 md:right-[6.7rem];
+          @apply bg-error text-error-content bottom-[2.4rem] right-0 md:bottom-0 md:right-[6.7rem];
         }
       }
     }
 
     .tk-preview-container {
-      @apply theme-border theme-bg mt-2 w-full rounded-xl border-2 p-2;
+      @apply border-base-300 bg-base-100 mt-2 w-full rounded-xl border-2 p-2;
     }
   }
 
   // 表情框样式
   .OwO-body {
     /* stylelint-disable-next-line scss/operator-no-unspaced */
-    @apply theme-card-bg theme-border absolute -left-3 top-9 z-20 hidden w-[calc(100vw-3rem)] max-w-[30rem] rounded-xl border-2 duration-300;
+    @apply bg-base-200/50 border-base-300 absolute -left-3 top-9 z-20 hidden w-[calc(100vw-3rem)] max-w-[30rem] rounded-xl border-2 duration-300;
   }
 
   .OwO-open .OwO-body {
@@ -144,10 +144,10 @@
     @apply flex flex-wrap items-center text-nowrap px-4;
 
     > li {
-      @apply hover:theme-card-bg-hl-trans flex h-8 cursor-pointer items-center px-3 text-center duration-300;
+      @apply hover:bg-primary/50 flex h-8 cursor-pointer items-center px-3 text-center duration-300;
 
       &.OwO-package-active {
-        @apply theme-card-bg-hl-trans;
+        @apply bg-primary/50;
       }
     }
   }
@@ -173,7 +173,7 @@
     }
 
     > .tk-comment {
-      @apply max-md:theme-card-bg max-md:theme-border p-4 max-md:rounded-xl max-md:border-2;
+      @apply max-md:bg-base-200/50 max-md:border-base-300 p-4 max-md:rounded-xl max-md:border-2;
 
       > .tk-avatar {
         @apply top-3;
@@ -190,7 +190,7 @@
 
     // 详细样式
     .tk-avatar {
-      @apply theme-border absolute top-0 h-8 min-h-8 w-8 min-w-8 overflow-hidden rounded-full border-2;
+      @apply border-base-300 absolute top-0 h-8 min-h-8 w-8 min-w-8 overflow-hidden rounded-full border-2;
     }
 
     .tk-main > .tk-row {
@@ -199,11 +199,11 @@
 
     .tk-meta {
       a {
-        @apply hover:theme-text-hl duration-300;
+        @apply hover:text-primary duration-300;
       }
 
       small {
-        @apply theme-text-second;
+        @apply text-base-content/50;
       }
 
       .tk-actions {
@@ -218,7 +218,7 @@
 
     .tk-action {
       .tk-action-link {
-        @apply theme-border relative flex items-center justify-center rounded-xl border-2 px-2 py-1 text-center duration-300;
+        @apply border-base-300 relative flex items-center justify-center rounded-xl border-2 px-2 py-1 text-center duration-300;
 
         .tk-action-icon-solid {
           @apply absolute left-2 opacity-0 duration-300;
@@ -237,7 +237,7 @@
         }
 
         &:hover {
-          @apply theme-card-bg-hl;
+          @apply bg-primary;
 
           .tk-action-icon-solid {
             @apply opacity-100;
@@ -254,7 +254,7 @@
       @apply flex flex-row gap-3;
 
       .tk-extra {
-        @apply theme-text-second theme-border flex flex-row items-center justify-center gap-2 rounded-md border-2 p-1 text-center text-xs;
+        @apply text-base-content/50 border-base-300 flex flex-row items-center justify-center gap-2 rounded-md border-2 p-1 text-center text-xs;
       }
 
       .tk-icon {
@@ -264,7 +264,7 @@
 
     .tk-replies .tk-content > span:first-child {
       // 回复提示(回复:xxx)样式
-      @apply theme-text-second text-xs;
+      @apply text-base-content/50 text-xs;
     }
 
     .tk-expand-wrap,
@@ -273,25 +273,25 @@
     }
 
     .tk-expand {
-      @apply theme-card-bg hover:theme-card-bg-hl-trans w-full cursor-pointer rounded-md py-1 text-sm duration-300;
+      @apply bg-base-200/50 hover:bg-primary/50 w-full cursor-pointer rounded-md py-1 text-sm duration-300;
     }
   }
 
   // 图标样式
   .tk-submit-action-icon {
-    @apply inline-block max-h-6 min-h-6 min-w-6 max-w-6 cursor-pointer fill-[var(--theme-text-color-light)] dark:fill-[var(--theme-text-color-dark)];
+    @apply fill-primary inline-block max-h-6 min-h-6 min-w-6 max-w-6 cursor-pointer;
   }
 
   .tk-action-icon {
-    @apply inline-block max-h-5 min-h-5 min-w-5 max-w-5 overflow-clip fill-[var(--theme-text-color-light)] dark:fill-[var(--theme-text-color-dark)];
+    @apply fill-primary inline-block max-h-5 min-h-5 min-w-5 max-w-5 overflow-clip;
   }
 
   .tk-icon {
-    @apply inline-block max-h-4 min-h-4 min-w-4 max-w-4 overflow-clip fill-[var(--theme-text-color-light)] dark:fill-[var(--theme-text-color-dark)];
+    @apply fill-primary inline-block max-h-4 min-h-4 min-w-4 max-w-4 overflow-clip;
   }
 
   .tk-tag {
-    @apply theme-border rounded-lg border-2 p-1 text-xs;
+    @apply border-base-300 rounded-lg border-2 p-1 text-xs;
   }
 }
 
@@ -299,7 +299,7 @@
   @apply mt-4 w-full text-right text-sm;
 
   a {
-    @apply text-[var(--theme-color-light)] dark:text-[var(--theme-color-dark)];
+    @apply text-primary;
   }
 }
 
@@ -314,7 +314,7 @@
     @apply relative flex h-full w-full items-center justify-center text-center;
 
     .tk-admin-close {
-      @apply absolute right-2 top-[0.65rem] z-50 h-8 w-8 fill-[var(--theme-text-color-light)] p-2 dark:fill-[var(--theme-text-color-dark)];
+      @apply fill-primary absolute right-2 top-[0.65rem] z-50 h-8 w-8;
     }
 
     > div {
@@ -342,18 +342,18 @@
       }
 
       a {
-        @apply theme-border theme-card-bg hover:theme-card-bg-hl mx-6 rounded-xl border-2 px-3 py-1 text-xs duration-300;
+        @apply border-base-300 bg-base-200/50 hover:bg-primary mx-6 rounded-xl border-2 px-3 py-1 text-xs duration-300;
       }
     }
 
     .tk-tabs {
-      @apply theme-border flex flex-row items-center justify-between border-b-2 px-4 text-center text-lg;
+      @apply border-base-300 flex flex-row items-center justify-between border-b-2 px-4 text-center text-lg;
 
       .tk-tab {
-        @apply hover:theme-card-bg-hl w-full cursor-pointer py-1 duration-300;
+        @apply hover:bg-primary w-full cursor-pointer py-1 duration-300;
 
         &.__active {
-          @apply theme-border-hl border-b-2;
+          @apply border-primary border-b-2;
         }
       }
     }
@@ -378,7 +378,7 @@
         @apply w-full;
 
         input {
-          @apply theme-border theme-card-bg w-full rounded-xl border-2 px-2 py-1;
+          @apply border-base-300 bg-base-200/50 w-full rounded-xl border-2 px-2 py-1;
 
           &:focus {
             outline: none;
@@ -387,7 +387,7 @@
       }
 
       select {
-        @apply theme-border theme-card-bg w-1/4 rounded-xl border-2 p-2;
+        @apply border-base-300 bg-base-200/50 w-1/4 rounded-xl border-2 p-2;
       }
     }
 
@@ -396,23 +396,23 @@
     }
 
     .tk-admin-comment-item {
-      @apply theme-border border-b-2 py-1;
+      @apply border-base-300 border-b-2 py-1;
     }
 
     .tk-admin-comment-meta {
       @apply flex flex-row flex-wrap items-center gap-2 text-start;
 
       .tk-avatar {
-        @apply theme-border h-8 w-8 overflow-hidden rounded-full border-2;
+        @apply border-base-300 h-8 w-8 overflow-hidden rounded-full border-2;
       }
 
       a {
-        @apply hover:theme-text-hl duration-300;
+        @apply hover:text-primary duration-300;
       }
 
       span:last-child,
       .tk-time {
-        @apply theme-text-second text-sm;
+        @apply text-base-content/50 text-sm;
       }
     }
 
@@ -424,7 +424,7 @@
       }
 
       input {
-        @apply theme-border theme-card-bg w-16 rounded-xl border-2 px-2 py-1;
+        @apply border-base-300 bg-base-200/50 w-16 rounded-xl border-2 px-2 py-1;
 
         &::-webkit-outer-spin-button,
         &::-webkit-inner-spin-button {
@@ -442,10 +442,10 @@
     }
 
     .tk-pagination-pager {
-      @apply hover:theme-card-bg-hl cursor-pointer rounded-md px-2 py-1;
+      @apply hover:bg-primary cursor-pointer rounded-md px-2 py-1;
 
       &.__current {
-        @apply theme-card-bg-hl;
+        @apply bg-primary;
       }
     }
 
@@ -459,10 +459,10 @@
     }
 
     details {
-      @apply theme-border w-full overflow-hidden rounded-xl border-2 duration-300;
+      @apply border-base-300 w-full overflow-hidden rounded-xl border-2 duration-300;
 
       summary {
-        @apply hover:theme-card-bg-hl-trans w-full px-3 py-1 text-start text-2xl duration-300;
+        @apply hover:bg-primary/50 w-full px-3 py-1 text-start text-2xl duration-300;
 
         &::marker {
           margin-right: 1.5rem;
@@ -471,7 +471,7 @@
 
       &[open] {
         summary {
-          @apply theme-card-bg-hl-trans;
+          @apply bg-primary/50;
         }
       }
     }
@@ -486,7 +486,7 @@
       }
 
       input {
-        @apply theme-border theme-card-bg w-full rounded-xl border-2 px-2 py-1;
+        @apply border-base-300 bg-base-200/50 w-full rounded-xl border-2 px-2 py-1;
 
         &:focus {
           outline: none;
@@ -494,7 +494,7 @@
       }
 
       .tk-admin-config-desc {
-        @apply theme-text-second whitespace-pre-wrap text-start text-sm;
+        @apply text-base-content/50 whitespace-pre-wrap text-start text-sm;
       }
     }
 
@@ -507,11 +507,11 @@
       }
 
       select {
-        @apply theme-border theme-card-bg w-full rounded-xl border-2 p-2;
+        @apply border-base-300 bg-base-200/50 w-full rounded-xl border-2 p-2;
       }
 
       input {
-        @apply theme-border theme-card-bg w-full rounded-xl border-2 px-2 py-1;
+        @apply border-base-300 bg-base-200/50 w-full rounded-xl border-2 px-2 py-1;
 
         &:focus {
           outline: none;
@@ -522,7 +522,7 @@
         @apply h-full w-full;
 
         textarea {
-          @apply theme-border theme-card-bg h-full w-full rounded-xl border-2 px-2 py-1;
+          @apply border-base-300 bg-base-200/50 h-full w-full rounded-xl border-2 px-2 py-1;
 
           &:focus {
             outline: none;
@@ -539,10 +539,10 @@
 }
 
 .el-input-group {
-  @apply theme-card-bg theme-border flex w-full flex-row items-center overflow-hidden rounded-xl border-2 text-sm;
+  @apply bg-base-200/50 border-base-300 flex w-full flex-row items-center overflow-hidden rounded-xl border-2 text-sm;
 
   div {
-    @apply theme-card-bg-hl-trans w-fit whitespace-nowrap px-2 py-1;
+    @apply bg-primary/50 w-fit whitespace-nowrap px-2 py-1;
   }
 
   input {
@@ -559,7 +559,7 @@
 }
 
 .el-button {
-  @apply theme-border theme-card-bg-hl text-nowrap rounded-xl border-2 px-2 py-1 text-center text-sm duration-300;
+  @apply border-base-300 bg-primary text-nowrap rounded-xl border-2 px-2 py-1 text-center text-sm duration-300;
 
   &:not(.is-disabled) {
     @apply hover:scale-105 hover:brightness-110 active:scale-95 active:brightness-90;
@@ -592,7 +592,7 @@
   }
 
   a {
-    @apply text-[var(--theme-color-light-darken)] underline decoration-dashed dark:text-[var(--theme-color-dark-lighten)];
+    @apply text-primary underline decoration-dashed;
 
     &[data-footnote-ref],
     &[data-footnote-backref] {
@@ -609,7 +609,7 @@
     }
 
     .copy-to-clipboard-button {
-      @apply theme-border theme-card-bg-hl rounded-md border-2 px-2 py-1 opacity-0 duration-300;
+      @apply border-base-300 bg-primary rounded-md border-2 px-2 py-1 opacity-0 duration-300;
     }
 
     &:hover .copy-to-clipboard-button {
@@ -624,7 +624,7 @@
   code:not(pre code) {
     padding: 0 0.25rem;
 
-    @apply theme-text-hl-contrast rounded-md bg-[var(--theme-color-light-trans-1d8)] dark:bg-[var(--theme-color-dark-trans-1d8)];
+    @apply text-primary bg-primary/10 rounded-md;
   }
 
   // 媒体元素
@@ -651,15 +651,15 @@
   blockquote {
     padding: 0.25rem 0.25rem 0.25rem 0.75rem;
 
-    @apply theme-border-hl my-2 rounded-sm border-l-4 bg-[var(--theme-color-light-trans-1d8)] dark:bg-[var(--theme-color-dark-trans-1d8)];
+    @apply border-primary bg-primary/10 my-2 rounded-sm border-l-4;
   }
 
   // 折叠块样式
   details {
-    @apply theme-border w-full overflow-hidden rounded-xl border-2 duration-300;
+    @apply border-base-300 w-full overflow-hidden rounded-xl border-2 duration-300;
 
     summary {
-      @apply hover:theme-card-bg-hl-trans w-full px-3 py-1 text-start text-2xl duration-300;
+      @apply hover:bg-primary/50 w-full px-3 py-1 text-start text-2xl duration-300;
 
       &::marker {
         margin-right: 1.5rem;
@@ -668,7 +668,7 @@
 
     &[open] {
       summary {
-        @apply theme-card-bg-hl-trans;
+        @apply bg-primary/50;
       }
     }
   }
src/styles/variables.scss
@@ -1,12 +0,0 @@
-$theme-color-light: #f69279;
-$theme-color-dark: #2e6c8b;
-$theme-bg-color-light: theme('colors.neutral.50');
-$theme-bg-color-dark: theme('colors.neutral.900');
-$theme-card-bg-color-light: $theme-bg-color-light;
-$theme-card-bg-color-dark: $theme-bg-color-dark;
-$theme-text-color-light: theme('colors.neutral.900');
-$theme-text-color-dark: theme('colors.neutral.100');
-$theme-text-color-secondary-light: theme('colors.neutral.600');
-$theme-text-color-secondary-dark: theme('colors.neutral.400');
-$theme-border-color-light: theme('colors.neutral.200');
-$theme-border-color-dark: theme('colors.neutral.800');
src/config.ts
@@ -10,7 +10,7 @@ import type {
   ToolBarConfig,
 } from './types/config';
 import I18nKey from '@i18n/I18nKey';
-import { getRandomPost, toggleSearch } from '@scripts/utils';
+import { getRandomPost } from '@scripts/utils';
 
 export const siteConfig: SiteConfig = {
   title: 'Astral Halo',
@@ -65,10 +65,7 @@ export const navbarConfig: NavbarConfig = {
       {
         icon: 'material-symbols:search-rounded',
         text: I18nKey.search,
-        onclick: {
-          id: 'search-btn',
-          function: toggleSearch,
-        },
+        onclick: 'search_modal.showModal()',
       },
     ],
   },
package.json
@@ -23,6 +23,7 @@
     "astro-icon": "^1.1.5",
     "astro-pagefind": "^1.8.0",
     "autoprefixer": "^10.4.20",
+    "daisyui": "^4.12.23",
     "postcss-load-config": "^6.0.1",
     "sass": "^1.83.4",
     "sharp": "^0.33.5",
tailwind.config.mjs
@@ -1,3 +1,6 @@
+import daisyui from 'daisyui';
+import themes from 'daisyui/src/theming/themes';
+
 /** @type {import('tailwindcss').Config} */
 export default {
   content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
@@ -8,6 +11,29 @@ export default {
       },
     },
   },
-  darkMode: 'selector',
-  plugins: [],
+  darkMode: ['selector', '[data-theme="dark"]'],
+  plugins: [daisyui],
+  daisyui: {
+    base: false,
+    themes: [
+      {
+        light: {
+          ...themes['light'],
+          primary: '#BC4D2E',
+          'primary-content': '#ffffff',
+          secondary: '#626437',
+          'secondary-content': '#ffffff',
+          accent: '#512620',
+        },
+        dark: {
+          ...themes['dark'],
+          primary: '#3C8CC7',
+          'primary-content': '#ffffff',
+          secondary: '#DB446B',
+          'secondary-content': '#ffffff',
+          accent: '#D29F60',
+        },
+      },
+    ],
+  },
 };