雑にピザトーストをつくるために、ピザソースと玉ねぎサラダ(そのまま食べられる)とスライスチーズを買ってきて BASE BREAD ミニ食パンに乗せて焼いた結果、それら全部を開けた包装を捨てまくっていた。ものを手に入れまくった結果あらゆる包装を捨てまくる瞬間に現代を感じて無常という気持ちになる。
z-indexバトル観戦
こんにちは、 id:hogashi です。 whywaita Advent Calendar 2022 - Adventar 3日目です。
z-index バトル
id:whywaita さんの好きなアルファベットは流石に Y ということでした。ありがとうございます。
やはり僕も id:whywaita さんの id を眺めていて、 w とか y とかから z-index を想起しまして、世の中の z-index バトルがどのように繰り広げられているのか見たいと思い、 GitHub で language が css と scss のコードを検索しました。 API でバリバリ検索したら 1000件しか検索できないということだったので、運良く 1000件にひっかかったコードの中から z-index のみなさまを雑に集計してみました。
ちなみに z-index とはスタイルシートのプロパティで、 z 方向つまり画面に垂直な方向の高さを示すようなものです。値が大きいほど手前に見え、値が小さいものが隠れる、といった具合。 z-index - CSS: Cascading Style Sheets | MDN
z-index が 100 より小さいもの
まず z-index が 100 より小さいものを見てみるとこう。 z-index 、負の数使えるんですね。 MDN にも "Negative values to lower the priority" って書いてある。バトルでいくと、 0 〜 5 では熾烈な戦いがありますね。人類は十進法を採用しました*1ので、なんとなく 10 ごとに山があるようにも見えます。
z-index が正の値のもの
1 〜 100 は上と被りますが、まとめて全部見ます。読者のみなさまのご想像通り、でかい値はでかいです。
対数グラフだと分かりづらいけど、 css での一位は 9999999 (10^7 - 1) 、 scss の一位は 999999 (10^6 - 1) ということで、桁がひとつ違う。まあ冒頭でも書いた通り雑に 1000件検索してるだけなので、まだ見ぬ巨大な z-index はあるかもしれないですね。値を眺めていくと、大体は十進数でキリの良い値だけど、変な値がちょこちょこあっておもしろい (666 とか)。いや〜バトルですね、これが見たかった。
実際の数字を見る
cssz-index の値 数 9999999 1 5000000 5 500000 2 100000 3 99999 2 65536 2 10100 1 10000 18 9999 233 9998 4 5000 2 3214 1 2020 1 2000 2 1900 1 1100 1 1009 1 1001 3 1000 23 999 236 997 1 996 1 895 1 643 1 600 3 500 18 416 1 301 1 300 5 250 2 240 5 233 1 214 1 200 6 199 2 197 3 190 1 150 2 145 2 139 2 110 1 102 2 101 5 100 29 99 2 98 1 86 1 80 2 74 3 57 2 52 2 51 2 50 3 45 2 42 1 40 2 37 2 33 1 32 4 31 2 30 6 25 4 23 1 20 15 19 2 15 3 14 3 13 3 12 5 11 1 10 27 9 16 8 10 7 8 6 41 5 282 4 41 3 293 2 327 1 403 0 289 -1 22 -2 4 -5 1 -9 1 -60 34scss
z-index の値 数 999999 7 100001 42 100000 1 99999 12 50000 2 9999 95 9998 2 5000 9 2010 1 2000 1 1500 1 1400 1 1300 1 1020 2 1000 4 999 102 998 8 700 1 666 1 500 6 400 4 300 2 250 1 200 1 110 1 100 26 99 5 98 8 90 1 70 1 69 1 60 1 50 2 49 1 40 4 39 1 30 7 28 1 27 1 22 2 21 1 20 6 15 1 10 32 9 13 8 3 7 3 6 4 5 111 4 16 3 128 2 181 1 335 0 134 -1 27 -6 1 -19 1 -20 1
統計
雑な統計をとるとこうなるっぽいです。スプレッドシートにすべてを任せているので分散がめちゃくちゃになっているの本当か?という気持ちになるけど、多分本当です。 (追記 2022/12/05: 計算するデータを間違えてたので直しました。引き続き分散はすごい)
css | scss | |
---|---|---|
合計 | 39506402 | 13753297 |
平均 | 15727.07086 | 10053.57968 |
中央値 | 3 | 3 |
分散 | 89591078363 | 5432903256 |
z-index バトルの頂点はどこか
stackoverflow で「ブラウザは 32bit 使ってるだろうし −2147483648 〜 2147483647 だと思う」って言ってる人はいます (html - Minimum and maximum value of z-index? - Stack Overflow) が、 MDN からたどって CSS の integer の定義を見に行くと、特に最大値などは書かれていないようでした https://w3c.github.io/csswg-drafts/css-values/#integers 。これからも z-index は高みを目指して切磋琢磨していくことでしょう *2。
あそびかた
こういう感じで雑に検索/集計しました。 GitHub の API は 1分おきに 30件取れて、 1000件取れるので、 34 回やると十分そうでした。 awk で TSV にして、スプレッドシートに貼り付けてグラフにしています。
ちなみにこれを書いている 2022/11/29 時点では、 GraphQL API にはコードの検索が実装されていませんでした。無念。
for page in {1..40}; do sleep 65 gh api -H "Accept: application/vnd.github.text-match+json" "/search/code?q=z-index+in:file+language:css&page=${page}" | tee ${page}.json done
cat *.json | jq .items[].text_matches[].fragment | perl -pe 's|z-index|\nz-index|g' | perl -pe 's|^.*?(z-index\s*:\s*[\-0-9]+)\s*;.*$|$1|g' | awk '{print $2}' | grep -vE '[^-0-9]' | sort -nr | uniq -c | awk '$2 ~ /-?[0-9]+/{ print $2 "\t" $1 }'
*1:まどろみ消去 MISSING UNDER THE MISTLETOE (講談社文庫) | 森博嗣 | 日本の小説・文芸 | Kindleストア | Amazon
*2:多分そんなことはなくて、バンドラなどが普及した現代では、変数などを使ったりしてちゃんと管理することで、 z-index のインフレを防ぐ工夫などが生まれていると思います
Renovateでつくられるpull requestのgithub .comへのリンクはドメインがtogithub .comに置き換えられている
Mend Renovate: Automated Dependency Updates を使うと、リポジトリに勝手に pull request を作ってくれるけど、その description に書かれる github.com
以下のリンクは、ドメインが ( github.com
ではなく) togithub.com
になっている。
これはバックリンクが作られてしまうことを防ぐためのものらしい。確かに、 GitHub 内で他の issue などのリンクを貼ると貼られた側にバックリンクが誕生して便利だけど、 Renovate のような機械的に大量に p-r を作るタイプのものだと困りそう。
ちなみに togithub.com
は誰が運用しているのか、と思って whois したら Redacted for Privacy になっていた。が、 Renovate のコードには「renovatebot redirector」と書かれているのを教えてもらった *1。 Renovate の持ち物っぽい。
renovate/index.ts at bef5030e11b227a62866c15cda1036b35273c9d2 · renovatebot/renovate · GitHub
// to be safe, replace all github.com links with renovatebot redirector
https://github.com/renovatebot/renovate/blob/bef5030e11b227a62866c15cda1036b35273c9d2/lib/modules/platform/github/index.ts#L1660
あと #123
とかはデフォルトではそのリポジトリの issue/p-r のリンクになってしまうので ​
が入っていたりするのもテクい……。
*1:id:polamjag ++
ワンタイムパスワード(OTP)のベストプラクティスじゃない入力フォームに出会う
こんにちは、 id:hogashi です。 masawada Advent Calendar 2022 - Adventar の 2日目です。
OTP 入力フォーム
なぜか id:masawada さんとたまにワンタイムパスワード (OTP) の話をする印象があります。偶然生成された「ホホンドホド」という文字列*1が TOTP で出そうな見た目じゃん、とか。
最近もまた微妙に使いづらい入力フォームに出会いました。そこで、世に存在するベストプラクティスとそれに沿わないフォームを見て、ベストたる所以をなんとなく感じてみる回をお送りします。結果的に GitHub がなんかむずい感じになっているという記事になりましたが、もちろん各サービスそれぞれ良いと思ってやっているはずなのであくまで個人の感想です。
まずベストプラクティスを見る
web.dev では、 input タグを:
type="text"
inputmode="numeric"
autocomplete="one-time-code"
にして使うのがよいと書かれています (フォーム以外に、 SMS の内容の書き方や WebOTP API についても書かれているので、一度見てみてください)。
あと前提として、値を 1つの input だけに入力する想定です。今回の記事ではどちらかというとそっちが重要。
それでは本題です
<input autofocus="" type="text" name="user-code-0" id="user-code-0" class="form-control js-user-code-field h1" maxlength="1" style="height: 2em; max-width: 1.5em; text-transform: uppercase" aria-label="User code 0" data-next="user-code-1"> <input type="text" name="user-code-1" id="user-code-1" class="form-control js-user-code-field h1" maxlength="1" style="height: 2em; max-width: 1.5em; text-transform: uppercase" aria-label="User code 1" data-next="user-code-2" data-previous="user-code-0"> <input type="text" name="user-code-2" id="user-code-2" class="form-control js-user-code-field h1" maxlength="1" style="height: 2em; max-width: 1.5em; text-transform: uppercase" aria-label="User code 2" data-next="user-code-3" data-previous="user-code-1"> <input type="text" name="user-code-3" id="user-code-3" class="form-control js-user-code-field h1" maxlength="1" style="height: 2em; max-width: 1.5em; text-transform: uppercase" aria-label="User code 3" data-next="user-code-5" data-previous="user-code-2"> <span class="h1">-</span> <input type="text" name="user-code-4" id="user-code-4" class="d-none" aria-label="User code 4" value="-" readonly=""> <input type="text" name="user-code-5" id="user-code-5" class="form-control js-user-code-field h1" maxlength="1" style="height: 2em; max-width: 1.5em; text-transform: uppercase" aria-label="User code 5" data-next="user-code-6" data-previous="user-code-3"> <input type="text" name="user-code-6" id="user-code-6" class="form-control js-user-code-field h1" maxlength="1" style="height: 2em; max-width: 1.5em; text-transform: uppercase" aria-label="User code 6" data-next="user-code-7" data-previous="user-code-5"> <input type="text" name="user-code-7" id="user-code-7" class="form-control js-user-code-field h1" maxlength="1" style="height: 2em; max-width: 1.5em; text-transform: uppercase" aria-label="User code 7" data-next="user-code-8" data-previous="user-code-6"> <input type="text" name="user-code-8" id="user-code-8" class="form-control js-user-code-field h1" maxlength="1" style="height: 2em; max-width: 1.5em; text-transform: uppercase" aria-label="User code 8" data-previous="user-code-7">
最近遭遇したのがこれ。 GitHub CLI でログイン (gh auth login
) するときに、 Login with a web browser
を選ぶと、ブラウザが開いてこの画面になります。 1文字打つと JavaScript でカーソルが右の input に進むタイプなのですが、入力中に input が空のままカーソルが右の input に進んでしまう、ということが起きました。厳しい。
よく観察すると、 keydown で文字を入力し、 keyup で次の input に進む、という挙動になっています。例えば "AB" と入力するとき、 A キーを押したまま B キーを押す (そしてどっちも離す) と:
# | イベント | input とカーソル |
---|---|---|
1 | 最初 | | _ _ _ - _ _ _ _ |
2 | A (keydown) | A _ _ _ - _ _ _ _ |
3 | B (keydown) | A _ _ _ - _ _ _ _ |
4 | A (keyup) | A | _ _ - _ _ _ _ |
5 | B (keyup) | A _ | _ - _ _ _ _ |
……という感じになって、 2つ目の input に B が打てないままカーソルが 3つ目に行ってしまうのでした。ちなみに手順 3 でも A しか入っていないのは maxlength="1"
だからですね (1つ目の input に 2文字入ろうとして無視される)。
あとせっかくコンソールにテキストでワンタイムパスワードが出るのに、コピペで入力できないのもつらい。いつもの input と同じ入力体験になってくれるか、あるいは素朴に全体で 1つの input を使ってくれると嬉しいな〜と思っています。
ちなみに
<input type="text" maxlength="1" autocomplete="none" value=""> <input type="text" maxlength="1" autocomplete="none" value=""> <input type="text" maxlength="1" autocomplete="none" value=""> <input type="text" maxlength="1" autocomplete="none" value=""> <input type="text" maxlength="1" autocomplete="none" value="">
Steam も↑の GitHub CLI の OTP の入力フォームとかなり近い形 (1桁ごとに input がある) になっているんですが、うまく作ってあるのか、↑のような困ったことは起きず、しかもメールに書かれたワンタイムパスワードをコピペで入力することもできる(!)ので、特に不便を感じたことがないです。とはいえアクセシビリティとかはどうなのか気になる。
ちなみに2
<input type="text" name="sudo_otp" id="totp" value="" autocomplete="off" autofocus="autofocus" class="form-control input-block js-verification-code-input-auto-submit mb-2" inputmode="numeric" pattern="([0-9]{6})|([0-9a-fA-F]{5}-?[0-9a-fA-F]{5})" placeholder="XXXXXX">
GitHub (Web ブラウザで使ってるとき) のフォームは、 HTML 的には大体ベストプラクティスに沿っているのですが、 6桁入力すると (Enter を押す前に) 即座に submit されるようになっています。 Enter の手間はないものの、最後の桁をミスったときは 6桁の入力が最初からになるのがちょっとつらいポイントですね。 submit はユーザのタイミングでやらせてほしい……。
むすび
僕が個人的に素朴なものを好きなのでバイアスがかかっているかもしれませんが、やはりユーザの体験は損なわないようにしたい、そしてベストプラクティスとされているものは説得力がありそう、という感じでした。あとここまで書いてなんですが、今後は WebAuthn や Passkeys などの OTP の入力が不要な認証方法に移り変わっていくものと思うので、そもそも入力の体験に気を払う必要はなくなっていくかもしれない…… (楽になるのはもちろん歓迎)。それでは良いお年を🎍
*1:string_random で[ヘッドホン]{5,6}から "ヘッドンホホ" を出そうとして遊んでた
VSCodeのUriでファイルシステム上のパスを見るときはpathよりfsPathがよさそう
VSCode で拡張機能を作ったりするとき、 vscode.Uri
でファイルシステム上のパスを見るには、 path
よりも fsPath
のほうが (OS に合わせたパスになるので) よさそう。
Mac では値が変わらないので気づかなかったけど、 Windows ではこういう感じで値が違う。
path: '/c:/Users/azuma/Documents/ghq/test/vscode-copy-github-permalink' fsPath: 'c:\\Users\\azuma\\Documents\\ghq\\test\\vscode-copy-github-permalink'
vscode.Uri
の型についているコメントを見ると説明されている *1。 https://github.com/microsoft/vscode/blob/58e7c7b8865e3c3ea77055461ffb5d656a7a46af/src/vscode-dts/vscode.d.ts#L1420-L1440
UNIX 系?のファイルシステムでは、パスの区切りがスラッシュだけど、 Windows ではバックスラッシュで、 fsPath
はそこを含めてプラットフォームに合わせたパスになっている、と書かれていそう。あと UNC paths というのは Windows ネットワークにおけるパスとしてそういう表記方法があるらしい。
* The string representing the corresponding file system path of this Uri. * * Will handle UNC paths and normalize windows drive letters to lower-case. Also * uses the platform specific path separator. * * * Will *not* validate the path for invalid characters and semantics. * * Will *not* look at the scheme of this Uri. * * The resulting string shall *not* be used for display purposes but * for disk operations, like `readFile` et al. * * The *difference* to the {@linkcode Uri.path path}-property is the use of the platform specific * path separator and the handling of UNC paths. The sample below outlines the difference: * ```ts * const u = URI.parse('file://server/c$/folder/file.txt') * u.authority === 'server' * u.path === '/shares/c$/file.txt' * u.fsPath === '\\server\c$\folder\file.txt' * ```https://github.com/microsoft/vscode/blob/58e7c7b8865e3c3ea77055461ffb5d656a7a46af/src/vscode-dts/vscode.d.ts#L1420-L1440
日記
VSCode の拡張機能で、開いているファイルに対応する GitHub 上での URL をコピーするというの *2 を作っている。不具合を issue *3 で教えてもらい、 Mac で直したら Windows で動かなくなっていて、 Windows でデバッグしたら path
と fsPath
の違いがあることに気づいた。
直したあと issue 上で動かなくなったよって教えてもらったときには Mac でしか試してなくて、再現しなくてなんでだろ……と思ったまま 2ヶ月経っていたのだけど、他の人にもそういえばその後どう?ってコメントしてもらって、 Windows で再現手順をもらったところで、そういえば Windows で動かないってことがあるのかも、と気づけた。ファイルシステムのパスを扱うときは OS の違いも意識したい……。
Gitのサブコマンド眺めたりgit help help見たりした
git help help
を初めて見た。 #102: Do Developers Really Know How to Use Git Commands? A Large-scale Study Using Stack Overflow – Misreading Chat で Git のサブコマンドの話をしていて、 140個ある!って言っててマジか、と思って git help --all
とかに辿り着いたりした。サブコマンドがジャンルごとに紹介されている。
$ git help --all --no-external-commands --no-alias | grep '^[^ ]' See 'git help <command>' to read about a specific subcommand Main Porcelain Commands Ancillary Commands / Manipulators Ancillary Commands / Interrogators Interacting with Others Low-level Commands / Manipulators Low-level Commands / Interrogators Low-level Commands / Syncing Repositories Low-level Commands / Internal Helpers User-facing repository, command and file interfaces Developer-facing file formats, protocols and other interfaces
あとそういえばと思って git help help
見たらもちろんあった。
git-scm.com
よく見ると --man
の他に --info
とか --web
とかがあることがわかっておもしろい。 Google Chrome など好きなブラウザのコマンドか実行ファイルへのパス*1を .gitconfig の web.browser
に設定しておくとブラウザで HTML 形式のヘルプが見れる。例えば git help --web status
を見るとfile:///opt/homebrew/Cellar/git/2.38.1/share/doc/git-doc/git-status.html
が開かれていてそこにあるのか〜という感じ (ディレクトリを見るとたしかにたくさん入っている)。多分 git-scm.com で見れるものと内容は同じだと思う。
全然関係ないけど、 git help --all --no-external-commands --no-alias
(v2.38.1 時点) で見れるコマンドを全部 RegExp::Assemble *2 にかけるとこういう正規表現が得られる。見どころは c(at-file|he(ck(...|out(...|l(ean|i|one)|...
あたりで、 check 系がいっぱいと、 cherry 系があるのと、 clean/cli/clone の cl 三兄弟がいるのと、その他 c 始まりがたくさんある。
$ rassemble $(git help --all --no-external-commands --no-alias | grep '^ ' | awk '{ print $1 }' | sort | tr '\n' ' ') a(?:dd|m|nnotate|pply|rchi(?:mport|ve)|ttributes)|b(?:isect|lame|ranch|u(?:greport|ndle))|c(?:at-file|he(?:ck(?:-(?:attr|ignore|mailmap|ref-format)|out(?:-index)?)|rry(?:-pick)?)|itool|l(?:ean|i|one)|o(?:lumn|mmit(?:-(?:graph|tree))?|nfig|unt-objects)|redential(?:-(?:cach|stor)e)?|vs(?:(?:exportcommi|impor)t|server))|d(?:aemon|escribe|i(?:agnose|ff(?:-(?:files|index|tree)|tool)?))|f(?:ast-(?:ex|im)port|etch(?:-pack)?|ilter-branch|mt-merge-msg|or(?:-each-re(?:f|po)|mat-(?:(?:bundl|signatur)e|c(?:hunk|ommit-graph)|index|pa(?:ck|tch)))|sck)|g(?:c|et-tar-commit-id|it(?:k|web)|rep|ui)|h(?:ash-object|elp|ooks?|ttp-backend)|i(?:gnore|map-send|n(?:dex-pack|it|staweb|terpret-trailers))|l(?:og|s-(?:files|(?:remot|tre)e))|m(?:ai(?:l(?:info|map|split)|ntenance)|erge(?:-(?:(?:bas|(?:one-)?fil|tre)e|index)|tool)?|kt(?:ag|ree)|odules|ulti-pack-index|v)|n(?:ame-rev|otes)|p(?:4|a(?:ck-(?:objects|re(?:dundant|fs))|tch-id)|r(?:otocol-(?:c(?:apabilities|ommon)|http|pack|v2)|une(?:-packed)?)|u(?:ll|sh))|quiltimport|r(?:ange-diff|e(?:(?:ad-tre|bas|mot|rer)e|flog|p(?:ack|lace|ository-layout)|quest-pull|s(?:et|tore)|v(?:-(?:list|parse)|ert|isions))|m)|s(?:calar|end-(?:email|pack)|h(?:-(?:i18n|setup)|o(?:rtlog|w(?:-(?:branch|index|ref))?))|parse-checkout|t(?:a(?:sh|tus)|ripspace)|ubmodule|vn|witch|ymbolic-ref)|tag|u(?:npack-(?:file|objects)|pdate-(?:index|ref|server-info))|v(?:ar|er(?:ify-(?:commit|pack|tag)|sion))|w(?:hatchanged|(?:ork|rite-)tree)
実際頭文字で数えると c 始まりが一番多いようでおもしろい。
$ git help --all --no-external-commands --no-alias | grep '^ ' | awk '{ print $1 }' | cut -c1 | sort | uniq -c | sort -nr 25 c 18 s 17 r 16 m 16 f 14 p 8 d 7 a 6 i 6 g 5 v 5 u 5 h 5 b 4 l 3 w 2 n 1 t 1 q
ちなみに正規表現を Regexper で図にするとこうなった。長い。頭文字 c だけ抜粋したものを貼りつつ全部の図も details の中に入れておきます。
クリックして図の全体を見る (SVGです)
*1:Google Chrome の場所は特に Mac だとよくわからないけど Chrome Browser debug logs - Chrome Enterprise and Education Help とかを見るとわかる
*2:実際使ったのは itchyny/rassemble-go
CSSのcounters()で箇条書きの数字をカスタマイズできておもしろい
CSS に counters() というのがあって、 1.1 とかをカスタマイズできておもしろい。 counters() - CSS: Cascading Style Sheets | MDN
言
葉
庭
というのをシンボルに使うことにして、繋ぎを の
にするとこうなる。
@counter-style kotonohanoniwa { system: fixed; symbols: 言 葉 庭; suffix: " "; } ol { counter-reset: kounter; } ol li { counter-increment: kounter; margin-left: 30px; } ol li::marker { content: counters(kounter, 'の', kotonohanoniwa) ") "; }
実際に ol で見るとこう。
- 1
- 1, 1
- 1, 1, 1
- 1, 1, 2
- 1, 1, 3
- 1, 2
- 1, 2, 1
- 1, 2, 2
- 1, 2, 3
- 1, 3
- 1, 3, 1
- 1, 3, 2
- 1, 3, 3
- 1, 1
- 2
- 2, 1
- 2, 1, 1
- 2, 1, 2
- 2, 1, 3
- 2, 2
- 2, 2, 1
- 2, 2, 2
- 2, 2, 3
- 2, 3
- 2, 3, 1
- 2, 3, 2
- 2, 3, 3
- 2, 1
- 3
- 3, 1
- 3, 1, 1
- 3, 1, 2
- 3, 1, 3
- 3, 2
- 3, 2, 1
- 3, 2, 2
- 3, 2, 3
- 3, 3
- 3, 3, 1
- 3, 3, 2
- 3, 3, 3
- 3, 1
数字は何個目の箇条かを書いただけで、 MDN の例に倣うとこれも counters() で出せるっぽい。というか今は marker の contents として counters してるというだけで、どこに出してもいいわけだから、なんかおもしろい使い方ができそう。
追記) ブコメに書いてもらってて気づいたけど、 Safari だと @counter-style
が未実装でただの数字に見える。 MDN とかをよく見ると Chrome などでもまだ完全な実装ではないっぽい。 @counter-style - CSS: Cascading Style Sheets | MDN
Does not support
https://developer.mozilla.org/en-US/docs/Web/CSS/@counter-styleas a value for the symbols descriptor.
というかいずれ画像も使えるようになるのか、便利そう……。