語意化的 CSS 設定 (Contextual awareness) 減少 side effect

前幾天在 Hacker News 上看到 CSS-Tricks 上的文章「You want enabling CSS selectors, not disabling ones」這篇,在講 CSS 的設計問題,對應的 Hacker News 討論在「You want enabling CSS selectors, not disabling ones (css-tricks.com)」這邊。

文章裡面引用文章裡面提到的文章也都蠻值得看的:「You want enabling CSS selectors, not disabling ones (2021/03/08)」、「Axiomatic CSS and Lobotomized Owls (2014/10/21)」。

其中 2014 年那篇居然是 A List Apart 上的文章,好久沒看到了這個站了... 也發現居然不在 RSS/Atom feed 清單裡面,重新訂起來。

這邊拿 A List Apart 上面的圖來說明,出自「CONTEXTUAL AWARENESS」這個段落的例子。

在很多段落時,我們常使用 margin-top (或是 margin-bottom,例子可以自己變換) 來設定間距,也就是 (a) 的例子。但可以看到第一個元素就會「多出來」:

A List Apart 裡面提到的解法是 * + * (或是 p + p,看你怎麼選 CSS selector),也就是前面有相鄰的元素才需要設定 margin-top

回到 CSS-Tracks 上的文章,有些人會這樣指定 CSS (這邊用 margin-bottom,所以搭搭配的是 :last-child):

.card {
  margin-bottom: 1rem;
}

/* Wait but not on the last one!! */
.parent-of-cards :last-child {
  margin-bottom: 0;
}

也就是全部都先加上 margin-bottom,然後針對最後一個元素拿掉 margin-bottom。而另外的版本則是:

.card:not(:last-child) {
  margin-bottom: 1rem;
}

或是:

/* Only space them out if they stack */
.card + .card {
  margin-top: 1rem;
}

這樣就不用蓋來蓋去,可以降低 side effect:margin-bottom 可能會在其他地方指定,你設為 0 可能是不對的值;另外寫成兩組時 CSS 的優先順序其實是不同的,Mozilla 的 Specificity 可以參考,Specifishity 這個網站給了很有趣的 cheatsheet (你要先了解才能當 cheatsheet 用):

在文章最後面有提到 gap 這個用法,查了一下「CSS property: gap: Supported in Grid Layout」,看起來現代的瀏覽器應該是都支援了,不過如果要支援舊的瀏覽器的話就是問題...

另外順便提一下,早期大家會偏好用 + 是因為 IE7+,而 :last-child 則是 IE9+ 了:「CSS Selectors and Pseudo Selectors and browser support」。雖然現在看起都是時代的眼淚了,但可以了解一下 2014 年的時候為什麼會偏好 + 的設計。

用 CSS Selector 產生 RSS feed

Hacker News 首頁看到「Show HN: RSS feeds for arbitrary websites using CSS selectors (vincenttunru.com)」這個,程式在 GitLab.com 上的「Feed me up, Scotty!」這邊,另外這個專案名稱是在玩 Star Trek 的梗:「Beam me up, Scotty」,然後這邊講的 RSS feed 已經算是通稱了,實際上大家都是輸出 Atom

他用的設定檔格式是 TOML,文章給的範例:

[funfacts]
title = "Wikipedia — did you know?"
url = "https://en.wikipedia.org/wiki/Main_Page"
entrySelector = "#mp-dyk > ul li"
titleSelector = "b"
linkSelector = "b a"

[wikivoyage]
title = "Wikivoyage recommendations"
url = "https://en.wikivoyage.org/wiki/Main_Page"
entrySelector = ".jcarousel-wrapper .jcarousel-item"
titleSelector = "h2"
linkSelector = "h2 a"

feed-me-up-scotty/src/index.ts 這邊看起來是 TypeScript 專案,然後用 browser 帶起 Firefox 來,可以預期會吃不少資源...

另外 Hacker News 的討論裡有另外提到「Feed Creator」,看起來也是個不錯的專案,有免費版與付費版...

Visual regression testing

看了「Catching CSS Regressions and Visual Bugs in Continuous Integration」這篇,原來這個叫做 visual regression testing,可以拿來檢查視覺上的差異:

出自「Visual Regression Testing Tools」。

這種 pixel-by-pixel 的測試也可以包在 CI 裡面,至少記錄起來可以在之後查。

看起來有不少 open source 工具與付費的服務可以用,不過機器的記憶體都會需要大一點 (需要瀏覽器的 rendering engine)。

Chromium 的 :has() 實做進展

前陣子在「Chromiu 看起來正在實做 CSS4 的 :has()」這邊提到了 Chromium:has() 實做,對應的票在「Issue 669058: CSS selectors Level 4: support :has()」這邊,不過看起來當時沒有實做完整,而且有測出問題:

The following css/selectors  web tests are failing on below platform:

css/selectors/has-basic.html - Failing on chrome/edge/firefox/safari/webkit
css/selectors/parsing/parse-has.html - Failing on chrome/edge/firefox/safari/webkit

昨天又看到新進度了:「Supports all ':has' relative argument cases」。

Supports all ':has' relative argument cases

Currently the relative selector is not supported yet, so this CL
provides the relative argument cases as follows.
 - :has(:scope > <complex-selector>)
 - :has(:scope ~ <complex-selector>)
 - :has(:scope + <complex-selector>)

看起來補上了之前沒實做的部份。更完整的討論過程可以參考 Gerrit 的「Supports all ':has' relative argument cases」這邊。

沒問題的話應該就是時間的問題了,也許三個月到半年左右?

Chromium 看起來正在實做 CSS4 的 :has()

先前一直都有在盯著「Issue 669058: CSS selectors Level 4: support :has()」這個,主要是有這個功能會對於很多應用很方便,無論是對 designer 在設計上,或是對 javascript developer 的操作。

Chromium 上對應的 commit 在「Supports :has() pseudo class matching」這邊,可以看到目前只實做了一部分:

So this CL only supports :has argument selectors starting with
descendant combinator. Argument selectors starting with other
combinators are not supported yet.
 - .a:has(.b)    : Supported
 - .a:has(> .b)  : Not supported yet
 - .a:has(~ .b)  : Not supported yet
 - .a:has(+ .b)  : Not supported yet

但至少是個進展了...

GET 與 POST 的差異

看到這篇在講 HTTP (& HTTPS) 裡面 GET 與 POST 的差異,剛好把一些標準的定義拿出來翻一翻,算是複習基本概念:「Get safe」。

第一個基本概念主要是 idempotence (& idempotent),重複被呼叫不會造成狀態的再次改變:

Idempotence ([...]) is the property of certain operations in mathematics and computer science whereby they can be applied multiple times without changing the result beyond the initial application.

數學定義是這樣跑:

An element x of a magma (M, •) is said to be idempotent if:

x • x = x.

If all elements are idempotent with respect to •, then • is called idempotent. The formula ∀x, x • x = x is called the idempotency law for •.

這點在 HTTP 標準 (RFC 7231) 裡面的定義也類似:

A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.

第二個基本概念是 Safe method (也是在同樣的 RFC 裡被提到),主要的思想是 read-only,這也是文章作者的標題要講的事情:

Request methods are considered "safe" if their defined semantics are essentially read-only; i.e., the client does not request, and does not expect, any state change on the origin server as a result of applying a safe method to a target resource. Likewise, reasonable use of a safe method is not expected to cause any harm, loss of property, or unusual burden on the origin server.

然後標準的 HTTP method 是有定義的:

   +---------+------+------------+---------------+
   | Method  | Safe | Idempotent | Reference     |
   +---------+------+------------+---------------+
   | CONNECT | no   | no         | Section 4.3.6 |
   | DELETE  | no   | yes        | Section 4.3.5 |
   | GET     | yes  | yes        | Section 4.3.1 |
   | HEAD    | yes  | yes        | Section 4.3.2 |
   | OPTIONS | yes  | yes        | Section 4.3.7 |
   | POST    | no   | no         | Section 4.3.3 |
   | PUT     | no   | yes        | Section 4.3.4 |
   | TRACE   | yes  | yes        | Section 4.3.8 |
   +---------+------+------------+---------------+

不過文章裡面提到的第一個例子並沒有很好,POST 不保證 safe 沒錯,但不代表 safe operation 就不能用 POST。

這邊用 URI resource 的概念 (以及 SEO?) 或是用 Post/Redirect/Get 的概念來說明會比較好:

<form method="get" action="/search">
<input type="search" name="term">

不過文章後續提到的問題的確就是我自己都會犯錯的問題:

“Log out” links that should be forms with a “log out” button—you can always style it to look like a link if you want.

“Unsubscribe” links in emails that immediately trigger the action of unsubscribing instead of going to a form where the POST method does the unsubscribing. I realise that this turns unsubscribing into a two-step process, which is a bit annoying from a usability point of view, but a destructive action should never be baked into a GET request.

這兩個動作都會造成 server 端的狀態改變,不應該用 GET,而我自己常常忘記第一個... 這邊其實可以用 form 產生 POST 需求,並且用 css 效果包起來,達到看起來跟一般的連結一樣。

寫起來讓自己多一點印象,之後避免再犯一樣的錯誤...

GitHub 的 Dark Mode 辨識度太低的問題

GitHub 推出 Dark Mode 後我就馬上切過去,但用了兩天後覺得辨識度太差就換回來了,剛剛在 Hacker News Daily 上看到不只我抱怨這個問題:「GitHub Dark Mode is Too Dark」,在 Hacker News 上的討論也可以翻翻:「 GitHub Dark Mode is too Dark (karenying.com)」。

裡面分析了 SpotifyFacebookYouTube 以及 GitHub 的配色盤,然後算出對比度,可以看到 GitHub 的配色上的確是差了不少...

目前 GitHub 上的 Theme 功能是掛 beta,應該還是有機會改,看看會不會修的好一點...

用純 HTML + CSS 做出來的踩地雷...

一樣還是 Hacker News Daily 上看到的東西,不過這個東西主要就是趣味性為主而已。這次看到的是純 HTML + CSS 做出來的踩地雷 (Minesweeper),沒有 JavaScript 在內:「css-sweeper from PropJockey」。

自從 HTML + CSS3 證明是 Turing-complete 後,再加上 CSS 本身又一直加各種互動性質的操作,出現這些東西好像不太意外就是了 XDDD

依照他的說明,這邊用到的 CSS 技巧主要是 Space Toggle 這個技巧 (也就是 --toggler 這個),但試著找了對應的文獻說明居然沒翻到,有人可以給個 hint 嗎...

模擬 Windows XP 風格的 CSS

Hacker News 首頁上翻到的,先前提到的「模擬 Windows 98 風格的 CSS」是對 Windows 98 風格設計出的 CSS,接下來就有人也生出經典的 Windows XP 版本出來:「XP.css」。

XP.css started as a fork of 98.css (a fun project started by Jordan Scales) and is now trying to boilerplate the GUI to be able to theme it easily.