1 điểm bởi GN⁺ 9 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Bộ chọn và khai báo CSS về mặt cấu trúc khá tương đồng với Datalog, ở chỗ chúng chọn ra một tập phần tử đã tồn tại và áp thuộc tính lên kết quả đó, giống như cách Datalog truy vấn quan hệ và tạo ra sự kiện
  • CSS thông thường không hỗ trợ tính toán đệ quy dùng lại kết quả chọn làm điều kiện chọn tiếp theo, nên không thể trực tiếp biểu đạt việc trạng thái như theme dark lan truyền xuống hậu duệ rồi dừng lại ở ranh giới light
  • Trong CSSLog giả định, ta cho phép thêm class ảnh hưởng đến việc khớp bộ chọn, nhờ đó có thể lan truyền đệ quy trạng thái suy ra như .effectively-dark và lặp tính toán cho đến khi không còn kết quả mới
  • Cách tính này được giải thích bằng fixpoint và monotonicity của Datalog; muốn việc đánh giá lặp kết thúc hữu hạn thì chỉ được thêm sự kiện mới chứ không được xóa sự kiện cũ
  • Container queries của CSS thực tế cũng có thể đọc trạng thái của tổ tiên, nhưng không thể truy vấn trạng thái đệ quy đã được suy ra, nên dù CSS có tiến gần đến Datalog thì vẫn không vượt qua ranh giới của engine render trình duyệt

Tương ứng cơ bản giữa CSS và Datalog

  • CSS giả định có sẵn các đối tượng đã tồn tại, và trong bài này các đối tượng đó là phần tử HTML
    • Các phần tử như h1, a, div đã tồn tại bên ngoài CSS; CSS không khai báo mới chúng
    • Ví dụ đưa ra các phần tử có thuộc tính như class, id, data-custom-attribute
  • Bộ chọn CSS chỉ tới một tập có chung điều kiện, và có thể thu hẹp đối tượng bằng tên thẻ, id, class, giá trị thuộc tính
    • Các bộ chọn như div, #child, .awesome, [data-custom-attribute="foo"] xuất hiện làm ví dụ
    • Cũng có thể biểu diễn đối tượng bằng quan hệ vị trí trong phân cấp tài liệu, và tạo giao bằng cách kết hợp bộ chọn
  • Bộ chọn kết hợp như div.awesome thực hiện phép giao tập hợp, chỉ chọn các phần tử vừa là div vừa có .awesome
    • Khái niệm giao này về sau trở thành điểm then chốt tương ứng với join trong Datalog
  • Quy tắc CSS gắn bộ chọn với khai báo để áp giá trị thuộc tính lên tập đã chọn
    • Trong ví dụ div.awesome { color: red; font-size: 24px }, colorfont-size được đặt cho các phần tử đó
    • Trên trình duyệt, kết quả là văn bản lớn màu đỏ được render

Giới hạn của CSS và bài toán truy vấn đệ quy

  • CSS thông thường mạnh ở chỗ thay đổi thuộc tính nằm ngoài ngôn ngữ, nhưng không thể trực tiếp dùng kết quả thay đổi đó làm điều kiện chọn tiếp theo
    • Ta có thể đặt màu cho phần tử, nhưng ví dụ như div[color=red] dùng chính màu làm điều kiện bộ chọn sẽ bị trình duyệt từ chối
    • Một quy tắc áp lại color: blue lên phần tử đang có color: red sẽ trở nên mơ hồ về ý nghĩa
  • Ví dụ dark mode trong design system được nêu như một bài toán cần lan truyền trạng thái bắc cầu
    • Ta muốn áp viền focus màu trắng cho mọi phần tử tương tác bên trong một card có data-theme="dark"
    • Tuy nhiên, nếu ở giữa có data-theme="light" thì việc lan truyền phải dừng ở bên dưới điểm đó
  • Trong CSS thực tế, chỉ có thể xử lý một phần bằng cách chồng thêm các ngoại lệ
    • Dùng [data-theme="dark"] :focus { outline-color: white; } để tạo quy tắc cơ bản
    • [data-theme="dark"] [data-theme="light"] :focus { outline-color: black; } để đảo lại tại ranh giới light
    • Nhưng cách này đòi hỏi phải tiếp tục thêm quy tắc khi độ lồng tăng lên
  • Bài toán này cần định nghĩa quan hệ đệ quy, nhưng CSS không biểu đạt được điều đó
    • Ta cần một định nghĩa kiểu “effectively-dark nếu bản thân nó là dark, hoặc có tổ tiên effectively-dark mà giữa chúng không có tổ tiên effectively-light”
    • Bài gốc gọi đây là recursive relational definition và khẳng định CSS không thể biểu đạt nó

Cú pháp giả định của CSSLog

  • CSSLog được đưa ra như một phiên bản giả định, vẫn giữ bộ chọn và thiết lập thuộc tính như CSS thường nhưng còn có thể thay đổi cả các thuộc tính ảnh hưởng đến việc khớp bộ chọn
    • Ví dụ có cú pháp như class: +bar để thêm class
    • Cũng giả định có dạng như +<div class="baz"> để tạo phần tử con mới
    • Việc xóa phần tử chỉ được nói là “có lẽ không nên được phép”, không có giải thích thêm
  • Sau khi quy tắc div.foo chạy, cùng phần tử đó có thể bắt đầu khớp cả div.bar, nghĩa là kết quả thực thi quy tắc ảnh hưởng đến lần khớp tiếp theo
    • Từ điểm này, tính toán không còn kết thúc bằng một lượt áp xuôi duy nhất mà cần lặp
  • Khi chuyển ví dụ dark mode sang CSSLog, ta có thể lan truyền đệ quy class suy ra
    • Bắt đầu với [data-theme="dark"] { class: +effectively-dark; }
    • Lan sang phần tử con bằng .effectively-dark > :not([data-theme="light"]) { class: +effectively-dark; }
    • Và áp style cuối cùng bằng .effectively-dark :focus { outline-color: white; }
  • Quy tắc thứ hai được mô tả là lan truyền đệ quy cho đến ranh giới light, rồi dừng khi đạt tới trạng thái mong muốn
    • Bài gốc nói rõ rằng CSS hiện tại không thể làm được việc này, và ở cuối bài sẽ quay lại một vài cách lách gần giống

Cấu trúc của Datalog và sự giống nhau với CSS

  • Trong Datalog, các đối tượng được gọi là atom, và chúng tồn tại ngay từ lúc được nhắc tới
    • Các tên như alice, bob có thể dùng trực tiếp mà không cần khai báo riêng
    • Có một câu so sánh kèm theo với :symbols của Ruby
  • Tập hợp và quan hệ được biểu diễn bằng relations và tuples
    • parent(alice, bob) là một tuple trong quan hệ parent
    • parent được mô tả là tập các cặp “đối tượng thứ nhất là cha/mẹ của đối tượng thứ hai”
  • Biến được dùng cho khớp truy vấn và chọn tập
    • parent(bob, X) nghĩa là mọi X mà Bob là cha/mẹ
    • Trong ví dụ này, X được đánh giá thành caroldave
    • Theo quy ước, biến viết hoa còn atom và relation viết thường
  • Khi cùng một tên biến lặp lại, sẽ có join
    • mother(X, Y) :- parent(X, Y), woman(X). tạo quan hệ mẹ từ giao của tập cha mẹ và tập phụ nữ
    • Bài viết giải thích đây là giao giữa “mọi phụ huynh” và “mọi phụ nữ”
  • :- trong quy tắc Datalog được đọc là if; khi mọi điều kiện ở phần thân bên phải đều đúng thì sự kiện ở phần đầu bên trái sẽ được thêm là đúng
    • Dấu phẩy trong body được đọc là and
    • ancestor(X, Y) :- parent(X, Y). có nghĩa là nếu X là cha/mẹ của Y thì X là tổ tiên của Y
  • CSS và Datalog được so sánh là có cấu trúc tương tự nhưng đảo chiều hình thức
    • color(X, red) :- div(X), class(X, awesome). tương ứng với “màu của X là đỏ nếu X là div và có class awesome”
    • Nó được đối chiếu về nghĩa với CSS div.awesome { color: red; }
    • Bài gốc tóm lại rằng selector là body còn declaration là head

Đệ quy và sự kiện suy ra

  • Trong Datalog, “làm” một điều gì đó có nghĩa là suy ra sự kiện mới
    • Hệ thống hoạt động bằng cách thêm tuple mới vào quan hệ dựa trên các sự kiện hiện có
  • Ví dụ ancestor là hình mẫu điển hình của quy tắc đệ quy
    • ancestor(X, Y) :- parent(X, Y). biến quan hệ cha/mẹ trực tiếp thành quan hệ tổ tiên
    • ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y). mở rộng lên phía trên theo quan hệ tổ tiên của cha/mẹ
  • Quy tắc thứ hai chứa đệ quy tự tham chiếuancestor xuất hiện ở cả head lẫn body
    • Từ quy tắc này, các quan hệ tổ tiên gián tiếp như alice -> bob -> carol, alice -> bob -> dave được suy ra
  • Kết quả chạy ví dụ đưa ra năm sự kiện sau
    • ancestor(alice, bob)
    • ancestor(bob, carol)
    • ancestor(bob, dave)
    • ancestor(alice, carol)
    • ancestor(alice, dave)
  • SQL trước WITH RECURSIVE cũng không làm được loại tính toán này; bài viết nói tính năng đó xuất hiện vì nhu cầu tính toán đệ quy
    • Tuy vậy, bài gốc cũng nói cú pháp và ngữ nghĩa đệ quy của SQL không phải lúc nào cũng kết hợp tốt với các phần khác
  • Datalog không cần for loop mà engine vẫn tiếp tục tính cho đến khi mọi kết quả cần thiết đều xuất hiện
    • Phần tiếp theo nối điều này với khái niệm fixpoint

Fixpoint và tính đơn điệu

  • Cascade của CSS thông thường được mô tả là một lượt áp xuôi duy nhất
    • Trình duyệt đọc quy tắc, tính khớp selector, áp declaration rồi kết thúc
    • Không có vòng phản hồi
  • Trong CSSLog và Datalog thực tế, kết quả của quy tắc có thể lại thỏa điều kiện của quy tắc khác
    • Một quy tắc thay đổi thuộc tính, thuộc tính đó khiến quy tắc khác chạy lại, rồi lại ảnh hưởng về quy tắc đầu tiên
  • Một engine Datalog ngây thơ sẽ lặp áp quy tắc cho đến khi không còn sự kiện mới
    • Bắt đầu từ các base fact đã khai báo
    • Khớp body của mọi quy tắc với tập sự kiện hiện tại
    • Nếu khớp thì thêm sự kiện ở head
    • Có sự kiện mới thì lặp lại, không có thì dừng
  • Điểm dừng này được gọi là fixpoint
    • Tức là trạng thái mà áp lại mọi quy tắc cũng không tạo ra kết quả mới
  • Ví dụ ancestor được tóm tắt thành ba vòng
    • Ở vòng đầu, ba quan hệ tổ tiên trực tiếp được thêm từ các sự kiện parent
    • Ở vòng hai, hai quan hệ tổ tiên gián tiếp của alice được thêm vào
    • Ở vòng ba, không còn sự kiện mới nên đạt fixpoint
  • Lý do có thể kết thúc nằm ở monotonicity
    • Vì không xóa sự kiện mà chỉ thêm vào, tập sự kiện đã biết chỉ có thể lớn dần
    • Với tập sự kiện ban đầu hữu hạn và số khả năng suy ra hữu hạn, hệ thống sẽ dừng sau hữu hạn bước
  • Ngược lại, nếu cho phép xóa sự kiện, ta sẽ có tình huống kết quả về sau lật ngược kết quả trước đó, và tính chất này bị phá vỡ
    • Bài gốc gọi đó là Infinite Loop Land, và từ đó liên hệ rằng tốt hơn CSSLog không nên cho phép xóa phần tử
  • Bài còn chú thích thêm rằng trong hệ phân tán, monotonicity cũng liên quan đến khả năng đạt nhất quán mà không cần điều phối tốn kém

Vì sao điều này quan trọng

  • Việc so sánh CSS với Datalog cho thấy cùng một cấu trúc trong những lĩnh vực khác nhau
    • Cả hai đều có đối tượng, có cách truy vấn tập các đối tượng đó, rồi áp một điều gì đó hoặc tạo ra sự kiện mới từ kết quả
  • Datalog và Prolog đã xuất hiện từ thập niên 1970 trong cơ sở dữ liệu quan hệ và nghiên cứu AI thời kỳ đầu, rồi sau đó được tái tạo dưới nhiều hình thức khác nhau
  • Bài viết đưa ra nhận xét rằng nếu xây dựng một hệ thống có đối tượng, mô tả được tập hợp và làm được việc gì đó với các đối tượng đó, nó sẽ có xu hướng hội tụ về những điểm tương tự
  • Lĩnh vực cơ sở dữ liệu, logic programming và phát triển web frontend không thường xuyên được kết nối với nhau
    • Bài viết bày tỏ kỳ vọng rằng nếu chúng được kết nối nhiều hơn, có thể tạo ra điều gì đó mới

Container Queries và ranh giới của CSS thực tế

  • Thảo luận này cũng chạm tới tính năng CSS thực tế là Container Queries
    • Ta có thể áp style cho phần tử hiện tại dựa trên style của phần tử cha hoặc tổ tiên
    • Ví dụ được đưa ra là @container style(--theme: dark) { .card { background: royalblue; color: white; } }
  • Tuy nhiên, bài toán dark mode bắc cầu cần một phép tính mạnh hơn việc chỉ nhìn tổ tiên
    • Mỗi phần tử phải biết liệu chính nó có effectively dark hay không
    • Trạng thái đó phải lan truyền bắc cầu xuống hậu duệ
    • Và phải dừng lại ở ranh giới data-theme="light"
  • Container queries không thể đọc trạng thái suy ra
    • Ta có thể truy vấn giá trị custom property của tổ tiên, nhưng không thể hỏi trạng thái như effectively-dark đã được quy tắc khác tính sẵn
    • Nó chỉ đọc được trạng thái đã có trong DOM, chứ không thấy kết quả của tính toán đệ quy
  • Vì vậy, các truy vấn kiểu “áp dụng nếu có tổ tiên nào đó là dark theo quan hệ bắc cầu, và không có tổ tiên light gần hơn” là không thể triển khai vì cần đệ quy
    • Bài gốc nói rõ rằng container queries không có đệ quy
  • Bài viết năm 2015 nói về lý do element queries liên tục thất bại vì nguyên nhân tương tự
    • Nếu cho phép truy vấn lại thuộc tính mà truy vấn đó vừa thiết lập, có thể phát sinh loop, thậm chí vòng lặp vô hạn
  • CSS Working Group được mô tả là đã giải loại vấn đề này bằng việc giới hạn hướng luồng thông tin
    • Hậu duệ có thể truy vấn thông tin của tổ tiên nhưng chiều ngược lại thì không được phép
    • Nhờ vậy vẫn giữ được tính hữu hạn mà không cần ngữ nghĩa fixpoint
    • Thông tin chỉ lan theo chiều đi xuống của cây và không tạo thêm base fact mới
  • Bài gốc mô tả dòng tiến hóa này như việc CSS tiến gần đến một Datalog engine nhưng cố ý không đi đến tận đó
    • CSSLog đứng về phía cho phép chu trình và đánh giá đến fixpoint
    • Còn CSS thực tế dừng lại ở chỗ nó là engine render của trình duyệt chứ không phải incremental relational database engine

Khả năng theo hướng khác

  • Thay vì đưa ngữ nghĩa Datalog vào trình duyệt, ta cũng có thể đi theo hướng đặt cú pháp kiểu CSS lên trên Datalog
    • Cú pháp Datalog như :-, dấu chấm câu, quy ước chữ hoa chữ thường, hay việc không có câu lệnh gán, có thể là rào cản với người dùng ngôn ngữ hiện đại
  • CSS vốn đã có một cú pháp xử lý trực tiếp cấu trúc cây
    • Nó có các combinator hậu duệ, con trực tiếp, anh chị em, nên biểu diễn quan hệ cha-con rất tự nhiên
    • Trong Datalog thông thường, các cấu trúc này thường phải được mã hóa sang dạng quan hệ theo cách hơi rườm rà
  • Bài viết nhấn mạnh rằng rất nhiều dữ liệu thực tế có dạng cây
    • JSON
    • AST
    • Hệ thống tệp
    • Sơ đồ tổ chức
    • XML
  • Nếu có một công cụ kết hợp đệ quy fixpoint, cú pháp kiểu CSS và quan hệ cha-con ngầm định, ta sẽ có thể viết truy vấn cây đệ quy bằng một ký pháp quen thuộc hơn
  • Theo bài gốc, dường như vẫn chưa ai thực sự tạo ra một công cụ như vậy
    • Bài khép lại bằng ý rằng có lẽ ai đó nên làm một thứ kiểu “CSSLog”, với một cái tên hay hơn

Chú thích

  • Có một chú thích về việc đơn giản hóa phần tử HTML
    • Tác giả lường trước phản biện rằng đối tượng CSS xử lý không nhất thiết chỉ là phần tử HTML, nhưng trong bài dùng phần tử HTML để đơn giản hóa giải thích
  • naive evaluation là không hiệu quả vì ở mỗi lần lặp lại tính cả những sự kiện đã biết
    • Cách cải tiến tiêu chuẩn được nhắc tới là semi-naive evaluation
    • Điểm chính là ở mỗi bước chỉ xét các sự kiện mới được suy ra
  • Bài cũng nói thêm rằng bản thân vòng lặp vô hạn không phải chuyện lạ với ngôn ngữ Turing-complete
    • Trong JavaScript ta cũng có thể viết while true {}
    • Nhưng trong ngữ cảnh hệ thống render của trình duyệt, ta muốn tránh việc trình duyệt treo vĩnh viễn chỉ vì logic của một website bị rối
  • Một chú thích khác bàn về cách lách bằng custom property inheritance trong CSS
    • [data-theme="dark"] { --effective-theme: dark; }
    • [data-theme="light"] { --effective-theme: light; }
    • @container style(--effective-theme: dark) { :focus { outline-color: white; } }
    • Cách này nhìn chung hoạt động cho trường hợp cụ thể này, nhưng cơ chế kế thừa không giống với transitive closure thực sự
    • Với các bài toán phức tạp hơn cần transitive closure dọc theo chuỗi thuộc tính khác ngoài cha-con, nó sẽ bị phá vỡ

1 bình luận

 
Ý kiến trên Hacker News
  • Bộ chọn CSS dễ viết hơn XPath rất nhiều
    Gần đây cũng có một bài nói về việc API DOM mới của PHP cho phép xử lý HTML và bộ chọn CSS một cách native rất dễ dàng. Trước đây phải chuyển CSS sang XPath
    [1] https://speakerdeck.com/keyvan/parsing-html-with-php-8-dot-4...
    Vì nó đã phát triển xoay quanh việc tạo kiểu cho trình duyệt, nên khá tiếc là không có các tính năng như chọn dựa trên nội dung văn bản kiểu XPath
    Tôi nhớ là trước đây đã từng có đề xuất, nhưng do có thể gây ra vấn đề hiệu năng trong ngữ cảnh render của trình duyệt nên không được đưa vào đặc tả

    • LLM cũng xử lý bộ chọn CSS khá tốt
      Khi làm một agent chỉnh sửa tài liệu, tôi hiển thị tài liệu dưới dạng HTML và để LLM chỉ định CSS selector để kéo những mảnh cần thiết vào ngữ cảnh, và nó hoạt động gần như kỳ diệu
    • Ở phía client, querySelector/querySelectorAll đã được dùng quá rộng rãi, nên thật vui khi DOM mới của PHP giờ cũng có thứ đó
      Mọi người có thể dùng đúng theo cách quen thuộc của mình
  • Sẽ hay hơn nếu có một cái tên để gọi tách biệt giữa cú pháp CSS và toàn bộ hệ thống gồm quy tắc, hàm, đơn vị... do CSSWG định nghĩa
    Mảng này có khá nhiều tiềm năng, nhưng nếu muốn bàn hoặc tìm hiểu các trường hợp ứng dụng khác thì có vẻ cuối cùng vẫn chỉ còn cách lục mã trên GitHub có nhúng parser CSS để xem mọi người đang làm ra những thứ kỳ lạ nào
    Tôi cũng đang nghịch một thứ giống engine template kỳ quặc, trộn giữa một ngôn ngữ markup nhẹ dựa trên node, bộ chọn CSS để biểu đạt thứ gì sẽ đi vào template, và một cú pháp na ná CSS để điều khiển cách ghép các mảnh này lại với nhau

    • Tôi nghĩ trong tiêu chuẩn thì chúng đã được tách khá rõ rồi
      https://www.w3.org/TR/selectors-3/
      Đặc tả DOM cũng tham chiếu tới nó
      https://dom.spec.whatwg.org/#selectors
      Vì vậy cách gọi chung CSS selector đã là đúng sẵn rồi, hoặc chỉ gọi là selector cũng được
      Tên DOM selector có thể nghe gọn hơn, nhưng nếu tính cả selector dùng trong CSS tĩnh hay trong các engine DOM khác ngoài JS engine (XML parser, PHP DOM API, v.v.) thì ngược lại có thể còn gây rối hơn
      Ngoài ra cũng có những selector đặc biệt gắn trực tiếp với việc render/điều hướng của trình duyệt như :hover hay ::target-text
      Tuy vậy, sẽ hữu ích nếu có một tên riêng cho tập con cú pháp truy vấn tối thiểu ít gắn với trình duyệt hoặc CSS hơn
  • Tôi nhớ tới https://github.com/braposo/graphql-css mà tôi từng thấy ở một hội nghị trước đây
    Dù là một dự án đùa vui, nó cho thấy rất rõ rằng việc mang một pattern sang ngữ cảnh khác rồi tái sử dụng có thể mở ra những điều bất ngờ

    • Cái này thú vị đấy
      Tôi đúng là đang thử mang pattern giữa các ngữ cảnh khác nhau theo kiểu như vậy
      Dù phần lớn sẽ chẳng đi đến đâu, nhưng theo tinh thần hacker thì khá là hấp dẫn
  • pyastgrep như ở https://pyastgrep.readthedocs.io/en/latest/ cho phép dùng bộ chọn CSS để truy vấn cú pháp Python
    Mặc định là XPath, ví dụ có thể dùng như pyastgrep --css 'Call > func > Name#main'

    • Cái này hay thật
      Nó gần như chạm đúng chính xác hướng mà tôi muốn chỉ tới
  • Tôi không rõ thứ này giải quyết kịch bản nào
    Ngay bây giờ cũng đã có thể thay đổi cha một cách có điều kiện dựa trên con. Ví dụ pre có padding mặc định là 16px, và nếu con trực tiếp là code thì có thể dùng &:has(> code) để đưa về 0

    • Thực ra ban đầu chuyện này xuất phát từ việc hai ý tưởng khác nhau trông có vẻ giống nhau, và tôi chỉ đang thử đẩy mối liên hệ đó theo nhiều hướng hơn
      Kết luận cũng không phải là "phải sửa giới hạn của CSS hiện đại", mà gần hơn với việc liệu có thể đặt một cú pháp giống CSS lên trên một hệ thống giống Datalog để khiến việc làm việc với dữ liệu dạng cây trở nên quen thuộc hơn với nhiều kỹ sư hay không
    • Điều đang nói ở đây không phải là giải quyết bằng một lần tính style, mà là cú pháp để chỉnh sửa chính dữ liệu nền của đối tượng khớp với selector
      Tức là đang nói tới chuyện thêm phần tử con hoặc thuộc tính mới vào DOM
  • LLM hiện tại thật ra không giỏi xử lý CSS lắm, nên ngược lại tôi lại muốn thử việc này để xem liệu nó có giúp LLM suy luận đơn giản hơn không

  • Chưa nghĩ ra giá trị sử dụng thực tế nào rõ ràng, nhưng dù sao vẫn ngầu

  • Ừm... tôi thấy cái này chẳng phải chỉ là JQ sao

  • Tôi có thích CSS ở một mức độ nào đó, nhưng ghét việc complexity creep ngày càng nặng
    Tôi hiểu lập luận rằng ngôn ngữ lập trình sẽ mạnh hơn ngôn ngữ không phải lập trình, nhưng thay vì cứ tiếp tục làm HTML, CSS và JavaScript ngày càng phức tạp hơn, tôi cảm thấy thà có một thứ khác thay thế toàn bộ còn hơn
    Tôi cũng gần như không dùng các phần tử mới của HTML5 vì phần lớn tôi không hiểu chúng cần để làm gì. Cuối cùng tôi lại nghĩ nhiều container chỉ đơn giản là div có ID riêng, thậm chí còn từng muốn có một kiểu bí danh cho các ID đó để điều hướng href nội bộ
    Những thứ như [data-theme="dark"] [data-theme="light"] :focus { outline-color: black; } làm tôi mất quá nhiều thời gian để diễn giải trong đầu nên tôi không còn thấy nó thanh lịch và đơn giản nữa
    Trong khi đó h2 { color: red; } thì vẫn đơn giản
    Những biểu thức như ancestor(X, Y) :- parent(X, Y). thì tôi đã không muốn nghĩ tới rồi. :- rốt cuộc là cái gì vậy, nhìn như mặt cười
    Tôi đã dừng đọc ở @container style(--theme: dark) { .card { background: royalblue; color: white; } }
    Thật kỳ lạ khi một tiêu chuẩn từng hoạt động tốt lại có vẻ như ngày càng trở nên hỏng hóc theo thời gian

    • Ý tôi không phải là thêm cú pháp và ngữ nghĩa vào CSS, mà đúng hơn là lấy cắp ý tưởng từ CSS rồi tận dụng sự tương đồng với ngôn ngữ truy vấn logic/quan hệ để tạo ra một thứ mới
      Ví dụ, nếu diễn giải [data-theme="dark"] [data-theme="light"] :focus { outline-color: black; } thành giả mã kiểu tiếng Anh, thì nó gần với ý: nếu có X với data-theme="dark", và con của nó là Y với data-theme="light" đang ở trạng thái focus, thì đặt outline-color của Y thành black
      Vì vậy có thể viết kiểu Datalog là outline-color(Y, black) if data-theme(X, "dark") and parent(X, Y) and data-theme(Y, "light") and focused(Y)
      Tức là thay :- bằng if, và dấu phẩy bằng and
      Đi xa hơn nữa, cũng có thể viết như Y.outline_color := black if X.data-theme == dark and Y.parent == X and Y.data-theme == dark and Y.focused để làm cho attr(X, val) trông giống syntactic sugar kiểu UFCS như X.attr == val
      Nếu muốn trông giống họ ALGOL hơn nữa thì cũng có thể viết như forall Y { Y.outline_color := black if Y.data_theme == "dark" and Y.focused and Y.parent.data_theme == "light" }
      Ở đây Y được đưa vào một cách tường minh và một phép join được ngầm hóa để trông giống lập trình phổ thông hơn, nhưng trên thực tế, engine Datalog chỉ đang chạy kiểu vòng lặp này một cách hiệu quả mỗi khi phụ thuộc thay đổi thôi`