可能被忽略的"按鈕組件"細節

對於組件的開發,咱們首推的是在開始寫代碼以前必定要和設計師溝通。看看設計師對於咱們組件的拓展是怎麼思考的。以規避程序員和設計師拓展方式的不一致致使的設計師一小改程序員一大改的成本問題javascript

一般咱們須要作一個按鈕的時候,無非就從以前或者別人那裏把代碼拷貝過來,而後根據設計稿,改改顏色,字號什麼之類的就能夠了。css

但隨着項目擴張按鈕形態的增長,若是咱們一開始就不是全局的角度去設計組件,到後面就須要花更多的時間去思考按鈕的拓展性前端

按鈕命名

舉個例子好比咱們有一個名爲 .btn 的基礎藍色按鈕。而後設計又給到了一個紅色放到 header 區域中的下載按鈕。你會如何拓展這個樣式呢?java

  1. .btn-header :基於位置
  2. .btn-red :基於顏色
  3. .btn-download :基於功能

以上三種是咱們一般會用到的比較快速的解決方案。這三個方案之因此快速,是由於他們都是基於當下場景去思考的。咱們只是用代碼描述了這個按鈕和其它按鈕的區別而已。react

但是關鍵問題來了,既然不一樣點有這三個,我應該選擇哪個呢?若是要在這三個裏選擇一種,咱們會推薦顏色git

  • 首先,若是咱們 footer 裏面也須要放一個紅色的下載按鈕,咱們顯然不能使用 .btn-header 這個名字來拓展咱們的按鈕,而應該使用 .btn-footer
  • 再者,若是咱們在 header 裏面須要添加一個紅色的上傳按鈕,此時顯然咱們 .btn-upload 更知足咱們的需求;

但是這樣就達不到咱們要複用樣式的目的了。若是是基於顏色拓展的話,你依然可使用 .btn-red 這個樣式來表示在 footer中的紅色下載按鈕。由於咱們要複用的是樣式,而紅色自己就是一種樣式,天然拓展性會更好一點。再者按照咱們的實際開發的經驗,顏色的改動明顯是遠遠低於咱們位置和功能的。甚至有不少的項目,一旦主題色定下以後,基本上是不會改的。基於這個點推薦你們能夠看一下張鑫旭老師的基於CSS color屬性的靜態UI組件重構策略的這篇文章。程序員

若是你的項目追求的是易用性,那麼咱們是很是推薦基於顏色命名,這個理解成本是最簡單的。可是若是非要站在一些理論的至高點,在這個方案上挑毛病的話主要有如下三點:github

  1. 你們對於同一個顏色的理解不同
  2. 不一樣的項目之間主色是不同的,也就是說不能用一套方案適用全部項目,這比較不符合組件化的思惟
  3. 有可能會出現,一個項目中兩個紅色,那此時你會怎麼命名另外一個紅色的按鈕?.btn-red2?

固然這三個點都是有點吹毛求疵的了,在實際開發中幾乎不會遇到這樣的問題。bootstrap

按鈕設計應該回歸設計

此時就引出了另一個問題,咱們按鈕的 UI 是設計師給的,因此對於按鈕的拓展方式咱們是須要和設計師溝通的。就像上面的問題,若是設計師給到了兩個紅色的按鈕,那麼能夠和設計討論是否考慮用其它的顏色替代。瀏覽器

既然是要溝通,就不能全部的拓展邏輯都是徹底基於設計同窗的思惟方式來。咱們得拿出咱們的方案。好在的是在當下的環境中,已經有比較好的最佳實踐了。這其中首推的是目前最火的也是火了不少年的前端 UI 框架 Bootstrap。他們對於按鈕的封裝,也幾乎成爲了國際通用的拓展規則。

這邊基於Bootstrap 按鈕組件,結合咱們實際的經驗帶你們看看,按鈕的封裝和拓展邏輯。文中的代碼是爲了解釋原理的僞代碼,實際開發會爲了減小代碼量,不會寫得這麼囉嗦。

代碼結構

_var.scss      // 參數
_base.scss     // 基礎樣式 
_theme.scss    // 主題
_size.scss     // 尺寸  
_shape.scss    // 形狀
_status.scss   // 狀態
index.scss     // 引用以上全部文件
複製代碼

對於 _var.scss 這個文件實際上是沒有的,由於在實際的項目之中,整個項目會有統籌全局樣式的一個參數文件。因此這個文件每每是引用的全局的參數文件。

按鈕基礎樣式 「 _base.scss 」

.btn {
      /* a 連接默認爲inline元素,但也有可能顯示爲按鈕因此設置 inline-block 屬性 */
      display: inline-block; 
      
      /* 按鈕文字居中,特別是當咱們給了按鈕一個固定寬度的時候 */
      text-align: center;
      
      /* 按鈕文字不換行 */
      white-space: nowrap; 
      
      /* 去掉能夠用鼠標選中按鈕上的文字功能,沒有這個屬性選中的時候會出現一個比較難看的半透明框 */
      user-select: none;  
      
      /* 爲非可選標籤,添加鼠標手型。大多數瀏覽器對於 a 標籤和 button 標籤默認是有這個屬性的,但其它標籤就不必定了 */
      cursor: pointer;
      
      /* 按鈕和文字混排的時候近似垂直居中 */      
      vertical-align: middle; 
      
      /* 讓padding 和 border 的寬度不影響按鈕大小,IE8 和 IE8 以上才兼容這個屬性 */
      box-sizing: border-box;
      
      /* 按鈕須要設計字體,這裏爲了保持統一建議和全站主字體保持一致 */
      /* 可是一般咱們在 css reset 中會作這一步的重置,因此這裏不須要了 */
      /* font-family:inherit; */
      
      /* 如下樣式根據實際設計狀況來編輯 */
     
      /* 按鈕圓角 */
      border-radius: 3px; 
      
      /* 去掉按鈕的默認邊框 */
      border: none 0; 
}          
複製代碼

對於按鈕基礎樣式,由於沒有涉及到按鈕的拓展性,因此你們的樣式基本都大同小異。

分類

按鈕基礎樣式設定完成以後,咱們要作的就是思考按鈕的拓展了。要拓展按鈕,首先要先看看按鈕的分類有哪些。

類型 類型細分
按鈕主題 主按鈕 primary, 次按鈕 secondary, 成功按鈕 success, 危險按鈕 danger, 警告按鈕 warning
按鈕大小 比大更大 largex, 大按鈕 large, 默認按鈕 default, 中號按鈕 middle, 小按鈕 small, 比小更小 smallx
按鈕形狀 連接按鈕 link, 幽靈按鈕 ghost, 膠囊按鈕 capsule, 塊狀按鈕 block
按鈕狀態 禁用 disabled, 鼠標移入 hover, 鼠標按下 active, 獲取焦點 focus, 加載 loading

基本上咱們按鈕主要能夠分爲以上四大類,而以上的幾大類又能夠互相的排列組合。

好比 disabled, warning, ghost, large 能夠表示一個禁用狀態下的警告幽靈大按鈕。

注:這裏的分類,只是爲了拆分代碼用,好比你要修改或者拓展主題就去theme.scss 這個文件當中修改。由於在實際使用能夠拓展的時候,咱們但願開發者和使用方其實弱化分類這個概念。

原生CSS

<button type="button" disabled class="btn _warning _ghost _large">warning按鈕</button>
<a href="javascript:;" class="btn _warning _disabled _ghost _large">warning按鈕</a>
複製代碼

在 CSS 規範 中有提到經過是用下滑線做爲前綴的命名規則。

組件化框架

<Button warning disabled ghost large>React按鈕</Button>
複製代碼

在相似 React 和 VUE 的場景中咱們推薦直接使用單屬性的方式拓展咱們們的組件,固然組件內部的實現能夠採用和原生 CSS 同樣的邏輯。

看到這裏有的同窗可能會對於咱們的拓展方式感到某些疑惑,由於從可讀性來講如下的方式顯然更加的優雅。

<Button theme="warning" status="disabled" shape="ghost" size="large">按鈕</Button>
複製代碼

對於這個問題咱們內部也有一些分歧,通常當團隊當中有分歧的時候咱們能夠參考一下目前已有的優秀的團隊是怎麼處理的。

<!-- 咱們的方案 -->
 <Button danger disabled ghost large>大的紅色的disabled狀態的幽靈按鈕</Button>
 
 <!-- ant design: github star 42k+ -->
 <Button type="danger" disabled ghost size="large">大的紅色的disabled狀態的幽靈按鈕</Button>
 
 <!-- material-ui: github star 44k+ -->
 <Button variant="outlined" color="secondary" disabled size="large">大的紅色的disabled狀態的幽靈按鈕</Button>
  <!--在 material-ui 中 variant="outlined" 表明幽靈按鈕 -->
 
 <!-- react-bootstrap : github star 14k+ --> <Button variant="outline-danger" size="lg" disabled>大的紅色的disabled狀態的幽靈按鈕</Button> <!-- 在 material-ui 中 variant="outline-danger" 表明紅色的幽靈按鈕 --> <!-- react.semantic-ui : github star 9k+ --> <Button basic color='red' fluid>大的紅色的disabled狀態的幽靈按鈕</Button> <!--在 react.semantic-ui 中 basic 表明幽靈按鈕 --> 複製代碼

看到這裏你們可能會比較凌亂,各個場的 API 風格真是百花齊放。我想說的是各個團隊有各個團隊本身的考慮,沒有絕對的好與壞,只能說適合本身的就是最好的。我這邊說說的咱們團隊這套 bool屬性 方案的優缺點:

優勢

  1. 原始: 由於咱們在尚未接觸組件開發的時候咱們就是使用的單 class 去控制的咱們的樣式( <a class="btn btn-danger btn-disabled btn-ghost btn-large">大的紅色的disabled狀態的幽靈按鈕</a>),在組件開發中咱們只是把 class 挪到了屬性當中;
  2. 乾淨: 一樣的功能,卻只須要更少的代碼;
  3. 方便使用方: 咱們只須要知道屬性名,而不須要記住它的分類。(好比我問 _ghost 這個按鈕應該是什麼分類?我相信對於不熟悉咱們按鈕的分類的同窗實際上是很難反應的出它是屬於咱們 shape 這個分類的);
  4. 方便開發者: 對於開發者來講,上面提到的不用糾結分類的問題一樣是適用。而且在本身想要拓展一些自定義按鈕樣式的時候,也不須要去考慮它應該屬於什麼分類,簡單的說,咱們的這套邏輯在代碼層已經弱化了分類的概念;
  5. 邏輯簡單: 咱們按鈕的不一樣屬性之間是能夠排列組合的。可是若是採用的 鍵值 的方的話,咱們很難實現同分類下的按鈕屬性組合。(好比咱們想同時使用 shape 的這個分類當中的 blockghost 這兩個狀態,固然有同窗可能會反駁說這兩個自己就不該該在一個分類中);

缺點

  1. 由於全是 bool 類型的屬性,寫校驗邏輯相對複雜;
  2. 對於某些不能排列組合的樣式容易寫錯,好比(<Button primary danger>按鈕</Button>), 這個很難被發現;
  3. 不太符合某些同窗的主觀意識,由於好像你們腦海裏對於顏色應該就是有一個分類的;
  4. 由於沒有了分類的概念,因此屬性值會很是多,至關於佔用了不少的屬性全局變量。
  5. 若是按鈕的主題,是來自於一個 themeProvider ,使用這種扁平化的方式就有點行不通。

固然咱們只是主要推薦使用 bool屬性 的方式去拓展咱們的按鈕,若是實在某些場景須要用到分類,咱們也不強制。

按鈕主題 「 _theme.scss 」

按鈕主題實際上是按照功能區分,只是設計師一般用顏色區分功能,因此主題也近似能夠看做是顏色的區分。

Bootstrap 是一個沒有特定產品的通用基礎框架,即便在按鈕設計極致收斂的狀況下,仍然有 Primary,Secondary,Success,Danger,Warning,Info, Light,Dark, Link 九種主題(在咱們看來 link 狀態的按鈕也有 primary 的做用,因此不一樣於 Bootstrap 咱們把 Link 歸類到了形狀shape這個分類中)。

這裏我以前說法有誤,Bootstrap 自己沒有提過主題分類這個概念。官網原文是這樣說的:

Bootstrap includes several predefined button styles, each serving its own semantic purpose, with a few extras thrown in for more control.

修正:Bootstrap 是一個沒有特定產品的通用基礎框架,即便在按鈕設計極致收斂的狀況下,仍然有 Primary,Secondary,Success,Danger,Warning,Info, Light,Dark, Link 九種預約義樣式

對於咱們本身的產品來講,這麼多的分類是不推薦的。咱們指望的是用更少的主題適應更多的場景,要達到這一點,也是須要多和設計師溝通的。以咱們的經驗, 主按鈕 primary, 次按鈕 secondary, 成功按鈕 success, 危險按鈕 danger, 警告按鈕 warning 這5種主題已經能涵蓋很大一部分場景了。

._primary{
    background-color:$c_primary;
    color:$c_primary;
}
.btn{ 
    color:#fff; 
}
.btn._ghost{
    background-color:transparent;
    border-color:1px solid;
}
.btn._link{
    background-color:transparent;
}
複製代碼

按鈕的主題色,在實際開發中咱們的顏色應該是基於全局的顏色參數去獲取的。對於全局顏色參數的命名,咱們推薦使用 c_ 前綴。

按鈕大小 「 _size.scss 」

比大更大 largex大按鈕 large默認按鈕 default中號按鈕 middle小按鈕 small比小更小 smallx...

在大小的數量上和主題邏輯是同樣的,建議使用更少的大小,適配更多的場景,咱們推薦使用大,中,小,加默認共計四種樣式。

固然若是要拓展大話,咱們建議經過相似衣服尺碼 xs, xl 添加 x 的方式進行拓展 _largex。

對於按鈕尺寸是設定邏輯,咱們建議聽從 Metiral Design 的 8 point 規則(尺寸控制在 8 像素的倍數,實在不能知足也應該至少是 4 的倍數)。

.btn{
	height: 40px;
	font-size: 16px;
	line-height: 24px;
	padding: 8px 16px;
}

.btn._middle{
	height: 32px;
	font-size: 14px;
	line-height: 24px;
	padding: 4px 12px;
}
複製代碼

對於大小,應該不僅是按鈕的高寬的變化,同時應該須要考慮到按鈕字號的變化,這樣纔會更加的協調。

按鈕形狀 「 _shape.scss 」

實心按鈕 fill, 連接按鈕 link, 幽靈按鈕 ghost, 膠囊按鈕 capsule, 塊狀按鈕 block...

按鈕的形狀,基本上業界經常使用的是以上五種方式,固然也不排除設計有定製的需求。

實心按鈕 fill

qq20181228-135519 2x
.btn._fill{
      color:#fff;
  }    
複製代碼

背景是主題色,文字是白色的按鈕,由於太經常使用因此通常做爲默認按鈕的樣式,因此在實際開發種咱們不會另起一個fill的屬性。

連接按鈕 link

qq20181228-135634 2x
.btn._link{
      background-color: transparent;
  }    
複製代碼

文字是主題色,背景爲透明的按鈕,雖然看起來是文本,可是它和其它按鈕佔據一樣大小的空間。

幽靈按鈕 ghost

qq20181228-135527 2x
.btn._ghost{
      background-color: transparent;
      border:1px solid;
      /* 兼容邊框增長引發的文字偏移 */
      line-height: 24px - 1px ;
  } 
複製代碼

文字和邊框是主題色,背景爲透明的按鈕。

border會默認使用文字的邊框顏色,這裏由於給按鈕設定了邊框,可是由於按鈕高度是寫死的,那麼意味着,這裏的文本會被往下推 1 個像素,這邊須要對於不一樣的按鈕作一個兼容。

膠囊按鈕 capsule

qq20181228-135602 2x
.btn._capsule{
    border-radius:100px;
  }    
複製代碼

左右兩邊是圓角的按鈕。

塊狀按鈕 block

qq20181228-135703 2x
.btn._block{
    display:block;
    width:100%;   
    
    /* 若是沒有給按鈕設定 box-sizing:border-box; 屬性,此處還應該去掉按鈕左右間距。 */
    /* padding-left:0; */
    /* padding-right:0; */
  }    
複製代碼

佔一行的按鈕。

按鈕狀態 [_status.scss]

:disabled 禁用狀態, :hover 鼠標移入, :active 鼠標按下, :focus 獲取焦點, ._loading 加載狀態...

按鈕處於一些臨界點的時候須要有一些特殊的狀樣式告知用戶,按鈕一般有以上的五個狀態。

/* 偷懶但簡潔 */ 
  .btn{
    transition:200ms;
  } 
  
  /* 繁瑣但性能更好 */
  .btn{
    transition:opacity 200ms, background-color 200ms, color 200ms;
  }  
複製代碼

由於按鈕的狀態切換,一般是從一個狀態到另外一個狀態,爲了讓這個狀態過分的更加天然,建議添加上 transition屬性。

:disabled 禁用狀態

qq20181228-153338 2x
.btn:disabled, .btn._disabled {
  /* 用css的方式讓元素不可被選中,不支持該屬性的須要用 js 阻止事件提交 */
  pointer-events: none; 
   
  /* 修改鼠標手型爲不容許 */
  cursor: not-allowed; 
  
  /* 修改透明度 */
  opacity: 0.5; 
}
複製代碼

按鈕不可用狀態,一般是某些只執行一次的操做,在操做完成以後的狀態。或者是須要某些特定觸發條件才能激活按鈕。 禁用狀態通常比正常按鈕看起來要更弱一點。最簡單的作法是下降透明度,這樣的好處是不用給每一個主題單獨去設定一個禁用狀態的顏色。 固然每一個主題單獨設定的視覺效果會更好。

而且 button 標籤若是有 disalbed 屬性還能夠阻止表單的提交。

:hover 鼠標移入

qq20181228-153722 2x
/* 鼠標移入狀態 */
.btn._primary:hover{
      background-color: darken($c_priamry,10%);
      color: darken($c_priamry,10%);
}
複製代碼

鼠標移入的狀態和 disabled 的效果會有點相反,一般會讓按鈕變得更重一點。爲了統一,咱們建議使用,css 預處理器的 darken 函數,來讓咱們的主題色,加深 10%

:active 鼠標按下

qq20181228-154404 2x
/* 鼠標移入狀態 */
.btn:active{
   transform:scale(0.98);
}
複製代碼

active 是緊接着 hover 的一個狀態,因此偷懶的話咱們忽略這個狀態,直接沿用 hover 的狀態。可是像作得更好的 Metiarl Design 他們採用的是漣漪水波的效果。而咱們這邊採用了更簡單的按下變小的邏輯「 感受很像你拿手把按鈕壓扁了 」。這個效果能夠和設計師溝通,權衡一下收益。

:focus 獲取焦點

button{ 
  outline:none;
}
複製代碼

focus 也是容易被你們忽略,甚至是爲了視覺效果而被捨棄。由於主流瀏覽器默認的 focus 狀態通常會是一個藍色漸變的陰影,一般來講設計師會認爲很差看。因而咱們常常會被要求用以上的代碼去掉瀏覽器默認的行爲。

對於這一點咱們是很是不推崇的,由於focus 對於無障礙訪問是很是重要的時候,當你的鼠標不能使用的時候,在有focus 的狀態下,別人也知道當前焦點的位置,從而也能進行操做。偷懶的作法是咱們什麼都不作,沿用瀏覽器的默認行爲,若是設計師說很差看,那請麻煩設計師拿出好看的替代方案,而不是直接去掉。

:loading 加載狀態

Loading

除了以上 4 個默認的瀏覽器按鈕狀態。一般狀況下,在按鈕按下以後等待 Ajax 請求結果的這段時間,咱們會經過一個額外的 Loading 狀態來告訴用戶此時正在加載。而且在這段時間,通常不容許用戶的二次操做,因此咱們此時的狀態建議是基於 disabled 的拓展。

此時採用 GIF 動圖會是一個體驗比較好的方案,可是由於咱們有多種主題和其它的狀態,咱們就須要對不一樣狀態作不一樣的 GIF 這不免有點繁瑣。因此咱們一般建議是使用CSS 動畫來處理這部分的邏輯。

在咱們項目裏,咱們採用了兩個小圈的效果「 相似於大白眨眼睛的效果 」。由於咱們能簡單操控的僞元素,有 before, after 因此在和設計師溝通的時候咱們但願只操做兩個元素。

高級按鈕

以上的按鈕都是咱們國際通用的標準化的形態。固然在咱們實際的工做狀態中,咱們不可能只有這麼單一形態的按鈕。不少時候咱們會有基於這些按鈕拓展出的新的高級按鈕。

對於這樣的按鈕,咱們不推薦直接在以上的標準的按鈕中兼容這些邏輯,哪怕只是在這個基礎按鈕中就加一行代碼就實現了拓展。而應該是繼承這個按鈕拓展出一個新的按鈕。

<Button small warning>基礎按鈕</Button>
<ButtonPop small warning popNum="3">高級氣泡按鈕</ButtonPop>
複製代碼
  • 代碼膨脹率 隨着咱們按鈕形態的豐富,基礎按鈕代碼膨脹率的也不會不受控的增加;
  • 代碼耦合 我要刪除一個高級按鈕,我只須要直接刪除這個高級組件按鈕就好,而不須要在基礎按鈕樣式中去找關於這個高級按鈕邏輯的代碼;

END

由於按鈕已經有了不少最佳實踐,咱們很容易進入拿來主義的誤區。而後在開發到後期的時候發現,這個最佳實踐和咱們實際項目不必定是 Match ,而後進入到縫縫補補的狀態。因此最後給你們的建議是前期仍是儘可能多花一點時間和設計溝通,和組內其它同窗溝通。就是適合本身的就是最好的。

參考文章

  1. 基於CSS color屬性的靜態UI組件重構策略
  2. bootstrap 按鈕組件
  3. ant design 按鈕組件
  4. material-ui 按鈕組件
  5. react-bootstrap 按鈕組件
相關文章
相關標籤/搜索