模式庫優先:一種管理 CSS 的方法
已發表: 2022-03-10在這篇文章中,基於我在多倫多 Smashing Conference 上的演講,我將描述我在過去兩年中採用的一種工作方法,它可以幫助我在我的項目中管理 CSS。
我將向您展示如何使用模式庫工具 Fractal,逐個組件地管理您的 CSS,同時允許您使用您已經熟悉的工具。 雖然這是對分形的介紹,以及我們選擇這個特定模式庫的原因,但這種工作方式很可能會轉移到其他解決方案。
我們的項目
我的公司有幾個產品——Perch 和 Perch Runway CMS 和 Notist,一種面向公眾演講者的軟件即服務應用程序。 這些產品完全不同,特別是考慮到 Perch 是自託管系統而 Notist 是 SaaS,但是它們都有很多用戶界面需要開發。 我們還擁有這些產品的所有相關網站和文檔,以及我們從事的其他工作,例如 24 Ways 網站。 在兩年前發現 Fractal 之後,我們將每個新項目——無論大小——都轉移到了 Fractal。
我們想要解決的問題
兩年前,當我開始為版本 3 重建 Perch UI 時,我開始研究模式庫解決方案。Perch 的一個特性是,您為網站上的內容輸出創建的模板成為管理 UI 的架構。 這意味著模板中使用的任何字段類型都需要能夠與任何其他字段類型一起存在。 我們不知道我們的客戶如何組合這些,並且有大量可能的組合。 它也不是一個“網站”,我不想嘗試將模式庫強加到為組織網站模式而設計的東西中。
由於 Perch 是自託管的——人們下載它並將其託管在自己的服務器上——我們需要盡可能使用最簡單的技術堆棧,以免在人們面前設置任何額外的進入障礙,其中許多人不熟悉使用CMS。 為了增加額外的樂趣,我們支持回到 Internet Explorer 9,但我打算使用大量的 Flexbox——因為這是在 Grid Layout 發布之前。
我也熱衷於避免使用伴隨著大量重新學習我們的工作方式並徹底改變我們的流程的工具。 任何額外的工具或對項目工作方式的改變都會帶來新的摩擦。 你可以解決一個問題,但如果你對工作方式做出重大改變,就會帶來一系列全新的問題。 在我們的例子中,我們以相當有限的方式使用 Sass,並使用 Gulp 進行處理。 我們的項目都沒有使用 Javascript 框架,我們只是在編寫 HTML、CSS 和 JavaScript。
分形完全符合我們的需求。 它與您的開發方式或您想要使用的工具無關。 對我們來說重要的是,它並沒有假設我們正在建立一個網站。 這個實驗非常成功,我們發現自己在每個大小項目中都使用了 Fractal,因為它使處理 CSS 的過程更加簡單。 即使是我自己創建的小型網站也經常從 Fractal 開始,因為使用模式庫的好處比你想像的要多,其中許多好處對於一個團隊和一個大團隊同樣有意義.
在我們考慮如何使用 Fractal 進行開發以及為什麼我認為它對小型項目和大型項目都有意義之前,讓我們先看看如何設置環境。
分形入門
使用 Fractal 最直接的方法是訪問 Fractal 網站並查看入門指南。 您首先需要全局安裝 Fractal,然後可以按照此處列出的步驟創建一個新的 Fractal 項目。
安裝好新項目後,在命令行切換到剛剛創建的文件夾並運行命令:
fractal start --sync
這將在端口 3000 啟動一個小服務器,因此您應該能夠在 Web 瀏覽器中訪問https://localhost:3000
並查看您的項目。
現在您的項目已經啟動並運行,在您最喜歡的文本編輯器中打開項目文件夾,並在components/example
下找到示例組件。 您將找到一個配置文件和一個名為example.hbs的文件。 example.hbs模板是您的組件的 HTML,您可以向其中添加更多 HTML,Fractal 將自動重新加載並顯示它。 將文件更改為:
<h1>This is my heading</h1> <p>{{ text }}</p>
您應該會看到標題出現在瀏覽器中。 配置文件可用於添加內容或配置您的組件。 如果您想從該文件中讀取標題文本,請將該文件編輯為如下示例:
title: Example component context: text: This is an example component! heading: My heading
現在更改您的example.hbs文件以讀取該文本。
<h1>{{ heading }}</h1> <p>{{ text }}</p>
添加其他組件
您可以按照示例組件的模式添加您自己的。 至少,您需要一個文件夾(組件的名稱)和一個使用相同名稱的.hbs文件。 如果要設置配置選項,可以添加配置文件。
組件可以嵌套到文件夾中,以便更輕鬆地定位特定組件,而如何構建文件夾完全取決於您。
注意:很容易發現自己花費大量時間擔心如何命名組件。 至少在 Fractal 中,重命名組件並將其重新組織到文件夾中很簡單。 您可以重命名或移動它們,Fractal 將更新以顯示新結構。 我發現理想的結構通常只會隨著我的發展而變得明顯,所以我一開始不用擔心太多,然後再把它固定下來。
添加 CSS 工作流
到目前為止,我們能夠創建 HTML 組件作為把手模板,以及一個用於插入數據的配置文件,但是,我們還沒有添加任何 CSS。 理想情況下,我們希望將每個組件的 CSS 添加到與其餘組件文件相同的文件夾中,然後將它們組合在一起。
我提到 Fractal 對您的工作流程做出的假設很少。 因此,與強迫您採用特定的工作方式相比,它的開箱即用功能要少得多。 然而,我們可以很容易地讓 Fractal 與 Gulp 設置一起工作。
結合 Fractal、Sass 和 Gulp
下面描述了使用 Gulp 和 Sass 創建單個輸出 CSS 文件的最小設置。 希望你可以按照這個過程做任何你在 Gulp 中通常會做的事情。 需要注意的關鍵是,其中大部分都不是 Fractal 特定的,所以一旦你讓 Fractal 部分工作,你可以按照相同的模式添加其他任何東西。 如果您熟悉另一個構建工具,那麼您很可能可以創建一個類似的過程; 如果你願意,並且樂於分享,請在評論中告訴我們。
首先進行一些設置,以下將使您能夠按照本教程中列出的代碼進行操作,您的 Sass 文件和輸出 CSS 的位置最終可能與我的不同。 關鍵是輸出 CSS 文件需要位於公共文件夾中的某個位置。
- 在 Fractal 安裝的公用文件夾中,添加一個名為css的文件夾。
- 在 Fractal 的根文件夾中,安裝添加文件夾assets ,其中是文件夾scss 。 在該文件夾中創建一個名為global.scss的 Sass 文件。 在該文件中添加以下行:
@import "../../components/**/*.scss";
- 在
example
組件目錄中創建一個名為example.scss的文件。 - 在 Fractal 項目的根目錄中創建gulpfile.js並添加以下代碼。
'use strict'; const gulp = require('gulp'); const fractal = require('./fractal.js'); const logger = fractal.cli.console; const sass = require('gulp-sass'); const sassGlob = require('gulp-sass-glob'); const plumber = require('gulp-plumber'); const notify = require('gulp-notify'); const path = require('path'); gulp.task('sass',function() { return gulp.src('assets/scss/**/*.scss') .pipe(customPlumber('Error running Sass')) .pipe(sassGlob()) .pipe(sass()) .pipe(gulp.dest('public/css')) }); gulp.task('watch', ['sass'], function() { gulp.watch([ 'components/**/*.scss', 'assets/scss/**/*.scss' ], ['sass']); }); function customPlumber(errTitle) { return plumber({ errorHandler: notify.onError({ title: errTitle || "Error running Gulp", message: "Error: <%= error.message %>", }) }); } gulp.task('fractal:start', function(){ const server = fractal.web.server({ sync: true }); server.on('error', err => logger.error(err.message)); return server.start().then(() => { logger.success(`Fractal server is now running at ${server.url}`); }); }); gulp.task('default', ['fractal:start', 'sass', 'watch']);
然後我安裝文件頂部列出的依賴項。 如果您要在命令行安裝它們,您將運行:
npm install gulp gulp-sass gulp-sass-glob gulp-plumber gulp-notify
sass
函數將資產中的 Sass 編譯成單個文件,並將其輸出到public
文件夾中。
gulp.task('sass',function() { return gulp.src('src/assets/scss/**/*.scss') .pipe(customPlumber('Error running Sass')) .pipe(sassGlob()) .pipe(sass()) .pipe(gulp.dest('public/css')) });
然後我創建了一個watch
函數,它將監視我的 Sass assets
以及單個組件,並將其編譯到公共文件夾中。
gulp.task('watch', ['sass'], function() { gulp.watch([ 'components/**/*.scss', 'assets/scss/**/*.scss' ], ['sass']); });
那是我的CSS建築。 我現在想做它,以便我可以運行 gulp,它會開始觀察 CSS 文件以及開始分形。 我通過創建一個 gulp 任務來運行分形啟動命令來做到這一點。
gulp.task('fractal:start', function(){ const server = fractal.web.server({ sync: true }); server.on('error', err => logger.error(err.message)); return server.start().then(() => { logger.success(Fractal server is now running at ${server.url}); }); });
最後,當我運行 gulp 和命令行時,我需要確保 Sass 構建和 Fractal 啟動運行:
gulp.task('default', 'fractal:start', 'sass', 'watch');
那是我完成的gulpfile.js 。 如果您將其添加到您的默認 Fractal 項目中,請確保文件夾針對所提到的路徑就位。 您應該能夠轉到命令行,運行gulp
,Fractal 將啟動。
我們可以通過在global.scss文件中添加一個變量來測試我們的 Sass; 您需要將其添加到包含組件的行上方,以便變量可用於這些組件。
$color1: rebeccapurple;
然後在example.scss
中為我們之前添加的 1 級標題添加規則:
h1 { color: $color1; }
如果一切設置正確,您應該會發現 public/css 中有一個.css文件,其中包含以下規則:
h1 { color: rebeccapurple; }
我們還需要做一件事,以便我們可以使用我們正在構建的 CSS 預覽我們的組件。 我們需要創建一個預覽文件,它將鏈接到公共文件夾中的樣式表。
在您的組件文件夾中,創建一個名為_preview.hbs的文件。
預覽文件本質上是一個 HTML 文檔,鏈接到我們的 CSS 和您需要包含的任何其他內容。 正文中有一個標籤{{ yield }}
,這是放置組件的地方。
<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Preview Layout</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="{{ path '/css/global.css' }}"> </head> <body> {{{ yield }}} </body> </html>
注意:公用文件夾還可以容納您需要在組件中顯示的任何其他資產,例如圖像、字體等。
模式庫作為真理的來源
正如我們所見,Fractal 可以構建我們的 CSS。 在我們的項目中,我們使 Fractal 成為我們為網站構建和處理 CSS 和其他資產的唯一地方。 這意味著我們的模式庫和站點或應用程序不會漂移。 如果人們開始編輯站點的 CSS 並且沒有將這些更改帶回模式庫,那麼在您部署站點之後就會發生漂移。 如果您可以將模式庫作為處理 CSS 的地方,那麼更改必須從那裡開始——這可以防止實時站點和庫之間的漂移。
我們在 Fractal 中構建所有內容,然後將這些公共資產複製到要部署的實時站點。 除了防止系統之間的偏差之外,它還使源代碼管理中的 CSS 管理變得更加容易。 當多人處理一個 CSS 文件時,合併衝突可能很難處理。 當人們在模式庫中處理單個組件時,您通常可以避免兩個人同時對同一個文件進行更改,如果他們這樣做,則只需整理一個小文件而不是所有 CSS。
使用模式庫優先方法來管理回退
我發現,工作模式庫首先使處理代碼中的回退比嘗試一次修復完整的站點或應用程序更直接,也更不壓倒性。 它還使我們能夠專注於最好的情況,並在新技術上發揮創造力,而不是因為擔心如何讓它在不支持的瀏覽器中正常工作而限制我們所做的事情。
我們可以看一個簡單的媒體對象組件案例,看看它是如何工作的。 接下來,在 Fractal 中的組件內創建一個媒體文件夾,並添加文件media.hbs和media.scss 。
從好的標記開始
您的起點應該始終是結構良好的標記。 在模式庫中,您可能會將此組件與一系列標記一起使用,例如,您可以使用一個組件,其內容在一個位置標記為圖形,而在另一個位置僅使用 div。 但是,您的內容應該以一種有意義的方式構建,並且可以從上到下閱讀。
這可確保您的內容在非常基本的級別上可訪問,但這也意味著您可以利用正常流程。 Normal Flow 是瀏覽器默認顯示您的內容的方式,塊元素在塊維度中一個接一個地前進,而內聯元素(例如句子中的單詞)沿著內聯軸運行。 對於許多正是您想要的內容,並且通過利用正常流程而不是與之抗爭,您可以在創建佈局時使您的工作變得更加容易。
因此,我的組件具有添加到media.hbs的以下標記。
<div class="media"> <div class="img"> <img src="/img/placeholder.jpg" alt="Placeholder"> </div> <h2 class="title">This is my title</h2> <div class="content"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis vehicula vitae ligula sit amet maximus. Nunc auctor neque ipsum, ac porttitor elit lobortis ac. Vivamus ultrices sodales tellus et aliquam. Pellentesque porta sit amet nulla vitae luctus. Praesent quis risus id dolor venenatis condimentum.</p> </div> <div class="footer"> An optional footer goes here. </div> </div>
你可以看到它是如何在 Fractal 中顯示的:
一旦我得到了我想要的標記,我就會在我想到的桌面顯示器上工作。 我將使用 CSS Grid Layout 和grid-template-areas
方法。 將以下內容添加到media.scss 。
img { max-width: 100%; } .media > .title { grid-area: title; } .media > .img { grid-area: img; } .media > .content { grid-area: bd; } .media > .footer { grid-area: ft; } .media { margin-bottom: 2em; display: grid; grid-column-gap: 20px; grid-template-columns: 200px 3fr; grid-template-areas: "img title" "img bd" "img ft"; }
我們現在有一個簡單的媒體對象佈局:
您可以在 Fractal 中做的事情是添加組件的變體。 您可能想要翻轉媒體對象,使圖像位於右側。
現在將 CSS 添加到media.scss以翻轉佈局:
.media.media-flip { grid-template-columns: 3fr 200px ; grid-template-areas: "title img" "bd img" "ft img"; }
有兩種創建變體的方法:基於文件和基於配置。 基於文件的最簡單,如果您的變體具有不同的標記,它也很有用。 要創建基於文件的變體,請在名為 media --flip.hbs (即文件名中的兩個破折號)的 media 文件夾中製作組件的副本。
該組件應該具有與添加到第一行的media-flip
類相同的標記,然後您將能夠看到這兩個版本。
<div class="media media-flip">
或者,在這種情況下,我們需要做的就是添加一個類,您可以使用配置文件創建一個變體。
如果您想這樣做,請刪除您的變體文件,然後添加一個名為media.config.json的配置文件,其中包含以下代碼:
{ "title": "Media Object", "context": { "modifier": "default" }, "variants": [ { "name": "Flipped", "context": { "modifier": "flip" } } ] }
然後修改media.hbs的第一行如下:
<div class="media media-{{ modifier }}">
注意:您可以添加任意數量的變體(查看變體文檔以了解更多信息)。
我們現在可能會考慮添加一些 CSS 來根據屏幕大小更改佈局。 包裝我們在媒體查詢中創建的佈局,以及為較小的設備創建單列佈局。
img { max-width: 100%; } .media > .title { grid-area: title; } .media > .img { grid-area: img; } .media > .content { grid-area: bd; } .media > .footer { grid-area: ft; } .media { display: grid; grid-column-gap: 20px; grid-template-areas: "title" "img" "bd" "ft"; } @media (min-width: 600px) { .media { margin-bottom: 2em; display: grid; grid-column-gap: 20px; grid-template-columns: 200px 3fr; grid-template-areas: "img title" "img bd" "img ft"; } .media.media-flip { grid-template-columns: 3fr 200px ; grid-template-areas: "title img" "bd img" "ft img"; } }
然後,就像我們在組件中管理較小設備的視圖一樣,我們可以管理不支持網格的舊瀏覽器的佈局。
在這種情況下,我將構建一個基於浮點的回退(這幾乎適用於任何舊版瀏覽器)。 我只會為更寬的屏幕尺寸擔心它,並讓組件在舊移動設備的正常流程中顯示。
在媒體查詢中,添加以下 CSS:
.media:after { content: ""; display: table; clear: both; } .media > .media { margin-left: 160px; clear: both; } .media .img { float: left; margin: 0 10px 0 0; width: 150px; } .media.media-flip .img { float: right; margin: 0 0 0 10px; } .media > * { margin: 0 0 0 160px; } .media.media-flip > * { margin: 0 160px 0 0; }
這應該整理非網格瀏覽器中的顯示。 對於支持網格的瀏覽器,您無需擔心浮動,即當浮動項變為網格項時,浮動會被移除。 任何利潤都會成為問題。 由於額外的邊距,支持網格的瀏覽器中的佈局現在將全部間隔開。
這是我們可以添加特徵查詢的地方,如果我們知道我們的瀏覽器支持網格,則刪除邊距。
@supports(display: grid) { .media > *, .media.media-flip > * { margin: 0; } .media .img, .media.media-flip .img { width: auto; margin: 0; } .media:after { content: none; } }
這樣我們的小組件就完成了。 雖然是一個簡單的例子——如果你需要一個後備,可能會說它根本不需要網格——它展示了我在所有項目中採用的方法,無論大小。
要將我的 CSS 文件投入生產,我們可以從公共文件夾中獲取 CSS 文件並將其添加到我們的生產站點。 您甚至可以編寫此過程的腳本,以便在構建時將其複製到您的站點文件夾中。
減少測試用例優先開發
我發現以這種方式工作的一個關鍵好處是,它確實使瀏覽器支持變得更容易。 不僅可以更輕鬆地查看該組件中包含哪些備用 CSS,而且如果我們在瀏覽器中遇到問題,也可以更輕鬆地調試它們。
當您與瀏覽器問題作鬥爭時,您通常會被告知要做的是創建一個簡化的測試用例。 將問題減少到表現出問題的最小事物。 模式庫中的組件通常已經非常接近簡化的測試用例。 當然,這比您在查看整個網站時嘗試調試問題要近得多。
除了使瀏覽器調試更容易之外,將您的後備代碼與 CSS 的其餘部分一起包含可以更容易在不再需要後刪除後備代碼,很明顯,這個後備代碼是針對這個組件的。 我知道刪除它不會改變其他任何顯示的方式。
這種易於組織我們的代碼的真正原因是 Fractal 即使在小型項目中也很有意義。 鑑於我們傾向於使用 Gulp 和 Sass(即使是在較小的項目中),將 Fractal 添加到組合中並不是很大的開銷。 我們不需要將其視為僅用於我們更大的項目,因為即使是小型站點也可能具有合理數量的 CSS。
見守則
我創建了一個 GitHub 項目,其中包含文章中提到的所有代碼。 我建議按照文章中的描述設置 Fractal,然後從我的存儲庫中獲取任何位 - 例如 gulpfile 或預覽佈局。
作為附加參考並查看一些已發布的分形項目,我們有 Perch 模式庫的已發布版本,以及 24 種方式的模式庫(由 Paul Robert Lloyd 構建),您可以查看它們。 這些是非網站模式庫的好例子,也是用於網站的更傳統的模式庫。
你如何管理 CSS?
我真的很喜歡這種工作方式; 它使我能夠以一種直接、逐步增強的方式編寫 CSS。 根據項目的不同,我們可能包括更多的工具和文件處理。 或者,我可能正在構建一個簡單的站點,在這種情況下,設置將與我們在本文中看到的差不多——對 Sass 進行一些簡單的處理。 Fractal 意味著我們可以對大小站點、Web 應用程序或網站採用相同的流程。 這意味著我們總是可以以熟悉的方式工作。
這對我們有用,我希望這篇文章能給你一些實驗的東西。 但是,我很想知道您和您的團隊在項目中管理 CSS 的方式以及您嘗試過的方法的優缺點。 我特別想听聽任何使用另一種模式庫解決方案開發了類似流程的人的來信。 在評論中添加您的經驗。