iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
Vue.js

作為 Angular 專家探索 Vue 3 和 Svelte 5系列 第 21

第 20 天 - Github Card 專案 第三部分 - 樣式設計

  • 分享至 

  • xImage
  •  

樣式設計其實是這個練習中最簡單的部分。
步驟在不同框架中通常是相同的。

  • 安裝 tailwindcss 和 DaisyUI
  • 在 CSS 檔案中加入 DaisyUI 插件
  • card 佈局複製到模板中
  • 將靜態文字替換為 profile 的值
  • 使用 tailwindcss 實用類別 (utility classes) 取代自訂 CSS 類別

安裝

Vue 3 and SvelteKit

npm install tailwindcss@latest @tailwindcss/vite@latest daisyui@latest

在 Vite 中添加 Tailwind CSS

import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [tailwindcss(), ...other plugins...],
});

在專案中先安裝 daisyUI 插件

@import "tailwindcss";
@plugin "daisyui";

Angular 20 application

npm install daisyui@latest tailwindcss@latest @tailwindcss/postcss@latest postcss@latest --force

配置檔案

// .postcssrc.json
{
  "plugins": {
    "@tailwindcss/postcss": {}
  }
}

在專案中先安裝 daisyUI 插件

// src/style.css

@import "tailwindcss";
@plugin "daisyui";

應用 TailwindCSS 類別

GithubProfileList 元件中,可以將 TailwindCSS 的類別 (class) 應用到標題(header)元素上,例如:

<div class="p-[0.75rem] col-span-full text-center">
    <h1 class="text-3xl">Github Profile List (Angular Ver.)</h1>
</div>

header 的 CSS 類別可以從樣式表 (style sheet) 中移除,改為使用 Tailwind CSS 的類別 (class) 來設定樣式 (style)。

另外,可以利用 TailwindCSS 的類別 (class) 來設計 CSS Grid 佈局,簡化自訂 CSS。

Vue 3 Application

App 元件的模板中

<div class="grid grid-cols-2 pt-0 pb-0 pl-[2rem] pr-[2rem]">
    <GithubProfileList :usernames="usernames" />
</div>

SvelteKit Application

// +layout.svelte

<div id="app" class="grid grid-cols-2 pt-0 pb-0 pl-[2rem] pr-[2rem]">
    {@render children()}
</div>

Angular 20 application

AppComponent 中使用 @Component 裝飾器時,可以修改 styles 屬性。

為了在 CSS 類別 (class) 使用 @apply,必須在配置中以 @reference 的方式包含 src/styles.css 檔案,確保不會重複包含該 CSS。

styles: `
    @reference "../../../styles.css";
    
    :host {
        @apply grid grid-cols-2 pt-0 pb-0 pl-[2rem] pr-[2rem]
    }
`,

Grid 佈局已成功應用於所有框架

美化後的 Card 佈局

CSS 樣式在範例中大致相同,但透過不同框架的控制流程語法 (control-flow syntax) 和資料插值語法 (interpolation),語法上會有所差異。

Vue 3 Application

// GithubProfileCard.vue

<div class="card card-side bg-base-100 shadow-sm" v-if="profile">
    <figure class="basis-[30%] grow shrink">
      <img :src="profile.avatar_url" :alt="profile.name" />
    </figure>
    <div class="card-body basis-[70%] grow shrink">
      <h2 class="card-title">{{ profile.login }}</h2>
      <p>Name: {{ profile.name }}</p>
      <p>Bio: {{ profile.bio || 'N/A' }}</p>
      <p>Followers: {{ profile.followers }}</p>
      <p>Following: {{ profile.following }}</p>
      <p>Public Repos: {{ profile.public_repos }}</p>
      <div class="card-actions justify-end">
        <button class="btn btn-primary">
          <a :href="profile.html_url" target="_blank" rel="noopener noreferrer">
            <span class="text-white">View Profile</span>
          </a>
        </button>
      </div>
    </div>
  </div>
  <div v-else-if="error">Error: {{ error }}</div>

SvelteKit Application

// github-profile-card.svelte

<div>
    {#if profile}
        <div class="card card-side bg-base-100 shadow-sm">
            <figure class="basis-[30%] grow shrink">
                <img src={profile.avatar_url} alt={profile.name} />
            </figure>
            <div class="card-body basis-[70%] grow shrink">
                <h2 class="card-title">{ profile.login }</h2>
                <p>Name: { profile.name }</p>
                <p>Bio: { profile.bio || 'N/A' }</p>
                <p>Followers: { profile.followers }</p>
                <p>Following: { profile.following }</p>
                <p>Public Repos: { profile.public_repos }</p>
                <div class="card-actions justify-end">
                    <button class="btn btn-primary">
                        <a href={profile.html_url} target="_blank" rel="noopener noreferrer">
                            <span class="text-white">View Profile</span>
                        </a>
                    </button>
                </div>
            </div>
        </div>
    {:else if error}
        <div>
            <p>Error: {error}</p>
        </div>
    {/if}
</div>

Angular 20 Application

@let status = profileResource.status();
@if (status === 'loading') {
    <p>Loading profile...</p>
} @else if (status === 'error') {
    <p>Error loading profile: {{ error() }}</p>
} @else {
    @if (profile(); as profile) {
        <div class="card card-side bg-base-100 shadow-sm" v-if="profile">
            <figure class="basis-[30%] grow shrink">
              <img [src]="profile.avatar_url" [alt]="profile.name" />
            </figure>
            <div class="card-body basis-[70%] grow shrink">
              <h2 class="card-title">{{ profile.login }}</h2>
              <p>Name: {{ profile.name }}</p>
              <p>Bio: {{ profile.bio || 'N/A' }}</p>
              <p>Followers: {{ profile.followers }}</p>
              <p>Following: {{ profile.following }}</p>
              <p>Public Repos: {{ profile.public_repos }}</p>
              <div class="card-actions justify-end">
                <button class="btn btn-primary">
                  <a [href]="profile.html_url" target="_blank" rel="noopener noreferrer">
                    <span class="text-white">View Profile</span>
                  </a>
                </button>
              </div>
            </div>
          </div>
    } @else if (error()) { 
        @let message = error();
        <div>Error: {{ message }}</div>
    }
}

Github Repositories

Github Pages

資源


上一篇
第 19 天 - Github 卡片專案 第二部分 - 元件組合
下一篇
第21天 - 部署 Github 個人檔案專案到 Github Pages
系列文
作為 Angular 專家探索 Vue 3 和 Svelte 522
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言