iT邦幫忙

2024 iThome 鐵人賽

DAY 14
2
Modern Web

為你自己寫 Vue Component系列 第 14

[為你自己寫 Vue Component] AtomicDivider

  • 分享至 

  • xImage
  •  

[為你自己寫 Vue Component] AtomicDivider

分隔線(Divider)元件在 UI/UX 設計中扮是個重要的輔助角色,用於在視覺上區分不同的內容區域,增強版面結構的清晰度和可讀性。

在實際網頁開發中,大多數時候我們會選用 CSS Class 來達到相關的畫面需求,例如在 TailwindCSS / UnoCSS 中,我們可以透過 divide-xdivide-y 這類的 Utils Class 來達到 Divider 的效果。

TailwindCSS Divider

<div class="grid grid-cols-3 divide-x">
  <div>01</div>
  <div>02</div>
  <div>03</div>
</div>

但是在一些情況下,我們可能會需要更多的客製化功能,並且需要明確的無障礙可讀性來幫助使用者更明確地分辨出不同的區塊。這時候我們就需要 Divider 這個元件。

元件分析

元件架構

AtomicDivider 元件架構

  1. Divider:分隔線。
  2. Content:分隔線的內容,可以用於標記分個線所表示的功能或裝飾。

功能設計

在開始實作前,我們先研究各個 UI Library 的 Divider 元件是如何設計的。

Element Plus

Element Plus Divider

<template>
  <div>
    <span>What you are you do not see, what you see is your shadow.</span>
    <ElDivider content-position="left">Rabindranath Tagore</ElDivider>
    <span>
      My wishes are fools, they shout across thy song, my Master. Let me but
      listen.
    </span>
    <ElDivider>
      <ElIcon><StarFilled /></ElIcon>
    </ElDivider>
    <span>I cannot choose the best. The best chooses me.</span>
    <ElDivider content-position="right">Rabindranath Tagore</ElDivider>
  </div>
</template>

除了一般分隔線,Element Plus 提供了 content-position 來為 default slot 的內容定位,預設為 center,也可以依照需求設定為 leftright

Element Plus 也支援使用 direction="vertical" 來設定垂直分隔線。

Element Plus Divider Vertical

不過在垂直分隔線的情況下,default slot 的內容並不會被渲染。

Nuxt UI

Nuxt UI Divider Vertical

<template>
  <div class="space-y-4">
    <!-- 略 -->
  </div>
  <UDivider orientation="vertical">OR</UDivider>
  <div class="space-y-4">
    <!-- 略 -->
  </div>
</template>

在 Nuxt UI,我們可以透過 orientation 來設定 Divider 的方向,預設為 horizontal,也可以設定為 vertical

Nuxt UI 不論水平或垂直的 Divider 都支援顯示 default slot,但不支援靠左、靠右或是靠上、靠下的定位方式。

綜合以上並結合自身經驗,我們統整出 <AtomicDivider> 的功能:

  • 可以透過 orientation 設定 Divider 的方向。
  • 可以透過 text-align 設定 default slot 的內容定位。

定位的部分,在 Element Plus 使用 leftcenterright 這樣的方式,但因為希望 orientationvertical 時也可以支援,所以可以接受的名稱調整為 startcenterend

元件實作

首先,我們將需求中提到的功能整理成 props 的介面,我們會需要下列屬性:

名稱 型別 預設值 說明
orientation horizontal, vertical horizontal Divider 的方向
text-align start, center, end center default slot 的內容定位
interface AtomicDividerProps {
  orientation: 'horizontal' | 'vertical'
  textAlign: 'start' | 'center' | 'end'
}

const props = withDefaults(defineProps<AtomicDividerProps>(), {
  orientation: 'horizontal',
  textAlign: 'center'
})

首先我們來規劃模板的部分:

<template>
  <div
    v-if="$slots.default"
    class="atomic-divider"
    :class="[
      `atomic-divider--${props.orientation}`,
      `atomic-divider--${props.textAlign}`
    ]"
  >
    <span class="atomic-divider__wrapper">
      <slot name="default" />
    </span>
  </div>
  <hr
    v-else
    class="atomic-divider"
    :class="[
      `atomic-divider--${props.orientation}`
    ]"
  >
</template>

如果使用者有使用 default slot,我們就渲染一個 <div> 元素,分隔線的部分我們使用 CSS 繪製;如果使用者單純使用分隔線的話,我們就選擇 <hr> 元素渲染。

接著我們來處理 SCSS 的內容,我們可以使用偽元素 ::before::after 來繪製分隔線,它會長這樣子。

Atomic Divider Before &amp; After

div.atomic-divider {
  display: flex;
  justify-content: center;
  align-items: center;

  &::before,
  &::after {
    content: '';
    display: block;
  }

  &--horizontal {
    &::before,
    &::after {
      width: var(--atomic-divider-size, 50%);
      border-top: 1px solid lightslategray;
    }
  }

  &--vertical {
    flex-direction: column;

    &::before,
    &::after {
      height: var(--atomic-divider-size, 50%);
      border-left: 1px solid lightslategray;
    }
  }
}

現在我們已經涵蓋了水平與垂直置中對齊的情況,再來只要處理 text-alignstartend 這兩種情況。在上面我們預留了 CSS 的變數,所以我們只要在對應的 CSS Class 中設定變數的值。

.atomic-divider {
  &--end {
    &::before {
      --atomic-divider-size: 90%;
    }

    &::after {
      --atomic-divider-size: 10%;
    }
  }

  &--start {
    &::before {
      --atomic-divider-size: 10%;
    }

    &::after {
      --atomic-divider-size: 90%;
    }
  }
}

最後我們來處理沒有 default slot 的情況,也就是渲染為 <hr> 元素的情況。

hr.atomic-divider {
  border: none;

  &--horizontal {
    border-top: 1px solid lightslategray;
    width: 100%;
  }

  &--vertical {
    border-left: 1px solid lightslategray;
    height: auto;
  }
}

這樣就完成了我們的 <AtomicDivider> 元件了!

無障礙

在 HTML 中 <hr> 標籤有分隔器(separator)的語意在裡面,但當我們使用 <div> 復刻出一樣的視覺效果時,也別忘了為使用螢幕閱讀器的使用者提供相同的語意。

<template>
  <div
    class="atomic-divider"
    role="separator"
  >
    <!-- 略 -->
  </div>
</template>

但如果使用 <AtomicDivider> 只是為了美化 UI,那我們可以使用 role="presentation" 這個設定。使用 role="presentation" 意味著這個元素僅作為視覺裝飾,對無障礙技術來說是無意義的,不會被輔助技術解釋為分隔線。

<template>
  <AtomicDivider role="presentation" />
</template>

總結

在實現部分,通過使用偽元素 ::before::after,我們能夠簡單有效地繪製分隔線,並且搭配 CSS 變數讓我們不需要過多的設定,就能達到理想的效果。

Divider 在我們的網頁開發中幾乎無所不在,如果遇到只是用於美化 UI 的需求,能用 CSS 解決其實就很足夠了!但如果遇到需要更細部樣式調整,或是像 Nuxt UI 的範例那樣,需要明確分隔兩個區塊,這時候就非常推薦使用 <AtomicDivider> 這個元件,它同時能達到需求,也能確保使用螢幕閱讀器的使用者能正確地理解網頁的內容。

參考資料


上一篇
[為你自己寫 Vue Component] AtomicChip
下一篇
[為你自己寫 Vue Component] AtomicProgress
系列文
為你自己寫 Vue Component19
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言