1 poin oleh GN⁺ 22 hari lalu | 1 komentar | Bagikan ke WhatsApp
  • Struktur CSS yang memilih himpunan target dan menerapkan properti melalui selector dan rule mirip secara bentuk dengan Datalog, yang bekerja dengan himpunan dan aturan
  • Penggabungan selector seperti div.awesome membentuk irisan, dan di Datalog join serupa terjadi dengan cara mengulang variabel yang sama
  • CSS saat ini tidak bisa memakai hasil style yang telah dihitung sebagai kondisi seleksi lagi, sehingga sulit mengekspresikan kueri transitif rekursif atau propagasi berulang dari status turunan secara langsung
  • Datalog memperluas relasi dengan aturan rekursif dan evaluasi titik tetap sampai tidak ada fakta baru yang muncul lagi, dan berkat monotonicity perhitungannya bisa selesai dalam cakupan yang terbatas
  • CSS nyata memiliki fitur seperti Container Queries untuk membaca informasi leluhur, tetapi memilih arah yang mencegah feedback loop dan siklus, meski tetap menyisakan ruang untuk menggabungkan sintaks CSS dengan kueri rekursif

Struktur mirip antara CSS dan Datalog

  • CSS memiliki struktur berupa memilih himpunan target dan menerapkan aturan pada target yang dipilih
    • “Things” seperti elemen HTML ada lebih dulu, lalu selector menunjuk himpunan dengan sifat yang sama
    • Himpunan bisa dideskripsikan dengan selector seperti div, #child, .awesome, [data-custom-attribute="foo"]
    • Selector bisa digabung seperti div.awesome untuk membentuk irisan
  • Aturan CSS mengikat selector dan declaration untuk menetapkan properti seperti color atau font-size pada elemen yang dipilih
    • Namun properti ini umumnya mengubah status di luar bahasa itu sendiri, dan hasilnya tidak bisa dipakai lagi sebagai kondisi selector
    • Bentuk seperti div[color=red] yang mengueri ulang hasil style tidak diterima browser
  • Datalog bekerja dengan cara yang serupa, yakni melalui himpunan fakta dan penurunan berbasis aturan
    • Atom dan relation seperti parent(alice, bob) menjadi unit dasarnya
    • Variabel X, Y digunakan untuk memilih himpunan item yang cocok dengan kondisi
    • Jika variabel yang sama diulang untuk menghubungkan kondisi, terjadi join yang mirip dengan penggabungan selector di CSS
  • Struktur head(X, Y) :- body1(X, Z), body2(Z, Y) mirip dengan aturan CSS, hanya arahnya terbalik
    • Selector pada CSS lebih dekat ke body Datalog, dan declaration lebih dekat ke head
    • div.awesome { color: red; } berpadanan dengan color(X, red) :- div(X), class(X, awesome).

Kueri rekursif yang tidak bisa dilakukan CSS

  • Kondisi untuk memberi style terbalik pada semua elemen fokus di dalam data-theme="dark", tetapi berhenti jika data-theme="light" muncul di tengah, memerlukan kueri transitif
    • Dalam CSS nyata, hal ini hanya bisa ditangani sebagian dengan aturan seperti [data-theme="dark"] :focus dan [data-theme="dark"] [data-theme="light"] :focus
    • Jika tingkat nested bertambah, aturan harus terus ditambah, dan sulit mengekspresikan relasi rekursif secara langsung
  • Kondisi yang dibutuhkan adalah menentukan secara rekursif apakah sebuah elemen effectively-dark
    • Jika dirinya sendiri memiliki data-theme="dark", maka ia menjadi effectively-dark
    • Anak di bawah leluhur effectively-dark juga menjadi effectively-dark, selama tidak ada data-theme="light" di tengah
    • Berdasarkan status ini, style harus diterapkan ke .effectively-dark :focus
  • Dalam sintaks CSSLog hipotetis, aturan bisa menambahkan status turunan seperti class: +effectively-dark
    • .effectively-dark > :not([data-theme="light"]) akan mempropagasikan status ke anak
    • Aturan harus diulang secara rekursif sampai mencapai status target
  • Jenis propagasi rekursif seperti ini sulit diekspresikan dalam CSS saat ini
    • Di bagian akhir tulisan juga muncul beberapa cara untuk menirunya secara terbatas, tetapi itu bukan solusi umum dengan prinsip yang sama

Rekursi dan titik tetap di Datalog

  • Datalog bekerja dengan cara menurunkan fakta baru dari fakta yang sudah ada, dan secara bawaan menangani rekursi
    • ancestor(X, Y) :- parent(X, Y).
    • ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
  • Aturan ancestor memperluas relasi leluhur secara bertahap berdasarkan relasi orang tua
    • Dari parent(alice, bob) mula-mula muncul ancestor(alice, bob)
    • Lalu jalur seperti alice -> bob -> carol, alice -> bob -> dave juga diturunkan lebih lanjut
  • Perhitungan ini berjalan sampai selesai melalui evaluasi titik tetap tanpa perlu loop for eksplisit
    • Pada awalnya hanya fakta dasar yang dinyatakan yang digunakan
    • Body dari semua aturan diterapkan ke himpunan fakta saat ini untuk menambahkan head
    • Proses berhenti ketika tidak ada fakta baru yang muncul lagi
  • Alasan pendekatan ini selesai terletak pada monotonicity
    • Sistem hanya menambahkan fakta dan tidak menghapusnya, sehingga himpunan fakta yang diketahui hanya terus membesar
    • Jika dimulai dari himpunan fakta yang terbatas, jumlah fakta yang bisa diturunkan juga terbatas
    • Sebaliknya, jika fakta bisa dihapus, kesimpulan sebelumnya bisa terbalik dan jatuh ke loop tak berujung

Container Queries dan batas CSS nyata

  • Container Queries di CSS nyata memungkinkan aturan diterapkan berdasarkan style leluhur atau container
    • Bentuk seperti @container style(--theme: dark) { .card { background: royalblue; color: white; } } didukung
  • Namun contoh transitive dark mode memerlukan kondisi yang lebih kuat daripada sekadar membaca leluhur
    • Setiap elemen harus tahu apakah dirinya sendiri effectively-dark
    • Status itu harus dipropagasikan secara transitif ke seluruh turunannya
    • Propagasi harus berhenti di batas data-theme="light"
  • Container Queries tidak bisa menangani syarat kedua
    • Custom property dari leluhur memang bisa dibaca, tetapi status turunan yang sudah dihitung oleh aturan lain tidak bisa dikueri ulang
    • Informasi yang memang sudah ada di DOM bisa dilihat, tetapi hasil perhitungan rekursif tidak bisa dijadikan kondisi selector
  • Tulisan terkait dari 2015 juga menyoroti bahwa element queries menabrak masalah yang sama
    • Jika properti yang ditetapkan lewat kueri bisa dikueri lagi, risiko loop dan pengulangan tak berujung akan membesar
  • CSS Working Group selama ini menghindari masalah ini dengan membatasi arah aliran informasi
    • Turunan boleh mengueri informasi dari leluhur
    • Feedback ke arah sebaliknya atau siklus ke style dirinya sendiri dicegah
    • Karena itu, perhitungan bisa tetap terbatas tanpa semantik titik tetap

Kemungkinan membalik sintaks CSS menjadi bahasa kueri rekursif

  • Alih-alih memasukkan semantik Datalog ke CSS, arah yang lebih realistis adalah meletakkan sintaks CSS di atas Datalog
    • Sintaks seperti :-, titik, dan atom tanpa deklarasi di Datalog menjadi hambatan masuk bagi banyak pengguna bahasa modern
    • CSS sudah memiliki sintaks selector yang kaya untuk menangani struktur pohon
  • Banyak data di dunia nyata berbentuk struktur pohon
    • JSON
    • AST
    • filesystem
    • bagan organisasi
    • XML
  • Di area seperti ini, menggabungkan sintaks bergaya CSS yang menangani relasi induk/anak secara implisit dengan rekursi titik tetap bisa berguna
    • Datalog umum mengharuskan struktur pohon ditulis ulang ke representasi relasional, yang cukup merepotkan
    • Jika intuisi selector CSS dibawa langsung ke kueri rekursif, lebih banyak programmer bisa mendekatinya dengan mudah
  • Alat dalam bentuk seperti ini belum benar-benar terlihat jelas
    • Nama “CSSLog” masih bersifat sementara, dan bisa saja nanti muncul bahasa dengan nama yang lebih baik
    • Masih ada ruang untuk menangani kueri pohon rekursif dengan notasi yang lebih akrab

Poin tambahan dan tautan referensi

  • Datalog muncul sejak 1970-an dalam konteks basis data relasional dan riset AI masa itu, lalu berulang kali muncul kembali dalam berbagai bentuk
  • Bentuk sederhana dari perhitungan titik tetap diperkenalkan sebagai naive evaluation, tetapi bisa tidak efisien karena fakta yang sudah diketahui dihitung ulang setiap saat
    • Semi-naive evaluation, yang hanya memanfaatkan fakta baru pada tiap tahap, disebut sebagai arah perbaikan yang mewakili
  • Monotonicity juga menghasilkan sifat yang berguna dalam sistem terdistribusi
  • Ada juga cara meniru transitive dark mode secara parsial dengan pewarisan custom property
    • [data-theme="dark"] { --effective-theme: dark; }
    • [data-theme="light"] { --effective-theme: light; }
    • @container style(--effective-theme: dark) { :focus { outline-color: white; } }
    • Untuk kasus khusus ini cara tersebut umumnya bekerja, tetapi tidak benar-benar menyediakan transitive closure secara umum

1 komentar

 
GN⁺ 22 hari lalu
Komentar Hacker News
  • Selector CSS jauh lebih mudah ditulis daripada XPath
    Baru-baru ini bahkan ada presentasi tentang API DOM baru di PHP yang memudahkan penanganan HTML dan selector CSS secara native. Dulu CSS harus dikonversi ke XPath
    [1] https://speakerdeck.com/keyvan/parsing-html-with-php-8-dot-4...
    Karena berkembang dengan fokus pada styling browser, agak disayangkan tidak ada fitur seperti pemilihan berdasarkan isi teks seperti di XPath
    Setahu saya dulu pernah ada usulan, tetapi tidak masuk ke spesifikasi karena bisa menimbulkan masalah performa dalam konteks rendering browser

    • LLM juga cukup mahir menangani selector CSS
      Saat membuat agen pengedit dokumen, saya menampilkan dokumen sebagai HTML lalu membiarkan LLM hanya menentukan CSS selector untuk menarik fragmen yang dibutuhkan ke dalam konteks, dan hasilnya bekerja nyaris seperti sihir
    • Di sisi klien, querySelector/querySelectorAll sudah digunakan sangat luas, jadi menyenangkan melihat itu kini masuk juga ke DOM baru di PHP
      Orang-orang bisa memakainya dengan cara yang sudah mereka kenal
  • Akan bagus kalau ada nama terpisah untuk membedakan sintaks CSS dan keseluruhan sistem seperti aturan, fungsi, dan unit yang didefinisikan CSSWG
    Ada cukup banyak potensi di sini, tetapi untuk membahas atau meneliti kasus penggunaan lain, sepertinya pada akhirnya kita harus mengaduk-aduk kode di GitHub yang memakai parser CSS untuk melihat hal-hal aneh apa yang dibuat orang
    Saya juga sedang mengutak-atik sesuatu yang mirip mesin template aneh, yang mencampurkan bahasa markup ringan berbasis node, selector CSS untuk menyatakan apa yang masuk ke template, dan sintaks mirip CSS untuk mengendalikan bagaimana potongan-potongan ini digabungkan

    • Menurut saya, di standar pemisahannya sudah cukup jelas
      https://www.w3.org/TR/selectors-3/
      Spesifikasi DOM juga merujuk ke sini
      https://dom.spec.whatwg.org/#selectors
      Jadi sebutan umum CSS selector memang sudah tepat, dan boleh juga hanya disebut selector
      Nama DOM selector mungkin terdengar lebih rapi, tetapi jika memikirkan selector yang dipakai di CSS statis atau di engine DOM lain di luar engine JS, seperti XML parser atau PHP DOM API, itu justru bisa lebih membingungkan
      Selain itu ada juga selector khusus seperti :hover atau ::target-text yang terikat langsung dengan rendering dan navigasi browser
      Namun, untuk subset sintaks query minimum yang kurang terikat ke browser atau CSS, akan berguna jika ada nama tersendiri
  • Ini mengingatkan saya pada https://github.com/braposo/graphql-css yang pernah saya lihat di konferensi dulu
    Itu proyek bercanda, tetapi saya suka karena menunjukkan dengan baik bahwa memindahkan pola ke konteks lain dan memakainya kembali bisa memungkinkan hal-hal yang tak terduga

    • Ini menarik
      Saya memang sedang mencoba membawa pola dari konteks berbeda seperti itu
      Meski kebanyakan mungkin tidak akan ke mana-mana, dari sudut pandang hacker ini cukup menarik
  • pyastgrep, seperti terlihat di https://pyastgrep.readthedocs.io/en/latest/, bisa memakai selector CSS untuk meng-query sintaks Python
    Default-nya adalah XPath, dan misalnya bisa seperti pyastgrep --css 'Call > func > Name#main'

    • Ini benar-benar bagus
      Hampir persis mengarah ke hal yang ingin saya tunjuk
  • Saya kurang paham skenario apa yang diselesaikan ini
    Saat ini pun parent bisa diubah secara kondisional berdasarkan child. Misalnya pre punya padding default 16px, dan jika child langsungnya adalah code, itu bisa dijadikan 0 dengan &:has(> code)

    • Sebenarnya ini pada awalnya lebih berangkat dari dua ide berbeda yang tampak mirip, lalu saya mendorong keterkaitan itu ke beberapa arah
      Jadi kesimpulannya lebih dekat ke "memakai sintaks mirip CSS di atas sistem mirip Datalog mungkin bisa membuat pekerjaan menangani data berbentuk tree terasa lebih akrab bagi lebih banyak engineer" daripada "memperbaiki keterbatasan CSS modern"
    • Yang dimaksud di sini bukan menyelesaikannya dengan satu kali perhitungan style, tetapi sintaks untuk memodifikasi data dasar itu sendiri dari target yang cocok dengan selector
      Artinya, pembicaraannya adalah tentang menambahkan elemen child atau atribut baru ke DOM
  • LLM saat ini cenderung tidak begitu bagus menangani CSS, jadi malah membuat saya ingin mencoba ini untuk melihat apakah LLM bisa bernalar lebih sederhana dengannya

  • Saya belum terlalu melihat kegunaan praktisnya, tapi tetap keren

  • Hmm... bukankah ini cuma JQ?

  • Saya cukup suka CSS sampai titik tertentu, tetapi saya tidak suka complexity creep yang makin parah
    Saya paham logika bahwa bahasa pemrograman menjadi lebih kuat daripada bahasa non-pemrograman, tetapi alih-alih terus memperbesar kerumitan HTML, CSS, dan JavaScript, rasanya lebih baik muncul sesuatu yang menggantikan semuanya
    Saya juga hampir tidak pernah memakai elemen baru HTML5 karena sebagian besar saya tidak paham kenapa itu diperlukan. Akhirnya saya menganggap banyak container hanyalah div dengan ID unik, dan saya bahkan berharap ada semacam alias untuk ID itu demi navigasi href internal
    Hal seperti [data-theme="dark"] [data-theme="light"] :focus { outline-color: black; } juga terasa butuh terlalu lama untuk dipahami di kepala, jadi tidak lagi terasa elegan dan sederhana
    Sebaliknya, h2 { color: red; } masih tetap sederhana
    Ekspresi seperti ancestor(X, Y) :- parent(X, Y). saja sudah membuat saya malas berpikir. :- itu sebenarnya apa, kelihatan seperti wajah tersenyum
    Saya berhenti membaca di @container style(--theme: dark) { .card { background: royalblue; color: white; } }
    Aneh rasanya melihat standar yang dulu bekerja baik makin lama justru seperti rusak

    • Maksud saya lebih dekat ke bukan menambahkan lebih banyak sintaks dan makna ke CSS, melainkan mencuri ide dari CSS lalu memanfaatkan kemiripannya dengan bahasa query logika/relasional untuk membuat sesuatu yang baru
      Misalnya [data-theme="dark"] [data-theme="light"] :focus { outline-color: black; } jika diurai ke pseudocode bergaya bahasa Inggris kurang lebih berarti, jika ada X dengan data-theme="dark" dan child Y dari X punya data-theme="light" serta sedang fokus, maka set outline-color Y menjadi black
      Jadi ini bisa ditulis dalam gaya Datalog sebagai outline-color(Y, black) if data-theme(X, "dark") and parent(X, Y) and data-theme(Y, "light") and focused(Y)
      Artinya :- diganti menjadi if, dan koma diganti menjadi and
      Lebih jauh lagi, ini bisa ditulis sebagai Y.outline_color := black if X.data-theme == dark and Y.parent == X and Y.data-theme == dark and Y.focused sehingga attr(X, val) tampak seperti syntactic sugar mirip UFCS semacam X.attr == val
      Jika ingin terlihat lebih seperti keluarga ALGOL, bisa juga menjadi forall Y { Y.outline_color := black if Y.data_theme == "dark" and Y.focused and Y.parent.data_theme == "light" }
      Di sini Y diperkenalkan secara eksplisit dan satu join dibuat implisit sehingga tampak lebih seperti pemrograman umum, tetapi pada praktiknya mesin Datalog yang akan menjalankan loop seperti ini secara efisien setiap kali dependensinya berubah`