ludic/examples/library/ui_css.ludic
Orkuncakilkaya 637d07400e feat(ui): ludic.ui grows toward HTML, CSS and React (Phase 0.U1-U4)
- Natives: ui_native(tag, measure, draw) makes an element the program draws itself; ui_fire
  runs its on-<event> with event.value. input, select and textarea have simple defaults.
- React:
  - keyed <each>, <let>, <provide> context through components, <fragment>;
  - named slots, default props on <component>;
  - on-mount / on-unmount;
  - inline text inside text elements.
- CSS:
  - custom properties and var();
  - position relative/absolute/fixed with insets and z-index;
  - em/rem/vw/vh and @media;
  - wrapping text and ellipsis, overflow;
  - + and ~ combinators, :nth-child(an+b), :checked, :active.
- Developing: errors with file:line, ui_errors(), ui_reload() keeping state, and an
  inspector-style ui_dump.
- view fields infer the type of a literal or a named function's result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 16:17:25 +03:00

26 lines
2.7 KiB
Text

# ui_css.ludic — ludic.ui's CSS without a renderer: custom properties and var(), absolute, fixed and
# relative positioning, em, rem and vw, @media, text that wraps, sibling combinators, :nth-child
# formulas, :checked, and overflow making a box scroll.
import "ludic.ui"
program UiCss {
numbers float
view Css {
wide = true
}
const LOOK: string = "screen { --gap: 6px; --accent: #ff8800 }\n.panel { position: relative; width: 300px; height: 200px; padding: 10px }\n.badge { position: absolute; right: 5px; bottom: 5px; width: 40px; height: 20px; background: var(--accent) }\n.corner { position: fixed; left: 0; top: 0; width: 10vw; height: 2rem }\n.nudge { position: relative; left: 7px; top: 3px }\n.big { font-size: 2em }\n.missing { color: var(--nope, #00ff00) }\n@media (max-width: 500px) { .wide-only { display: none } }\n@media (min-width: 500px) { .narrow-only { display: none } }\nli + li { margin-top: var(--gap) }\nh3 ~ p { color: var(--accent) }\nli:nth-child(2n+1) { color: #0000ff }\ninput:checked { opacity: 0.25 }\n.list { overflow: auto; height: 50px }\n.para { width: 120px }\n"
const PAGE: string = "<ui>\n<import src='look.lss'/>\n<screen name='css'>\n <div class='panel'><span class='badge'>!</span><p class='nudge'>moved</p><p class='big'>big</p></div>\n <div class='corner'/>\n <p class='wide-only'>wide</p><p class='narrow-only'>narrow</p>\n <ul><li>a</li><li>b</li><li>c</li></ul>\n <h3>h</h3><p>after</p><p class='missing'>fallback</p>\n <input type='checkbox' checked='true'/>\n <div class='list'><p>1</p><p>2</p><p>3</p><p>4</p></div>\n <p class='para'>a sentence long enough that it has to wrap onto several lines</p>\n</screen>\n</ui>\n"
entry {
ui_load_text(LOOK, "ui/look.lss")
ui_load_text(PAGE, "ui/css.xml")
ui_viewport(400.0, 300.0)
let root: UiNode = ui_nodes("css", view_css())
ui_place(root, 0.0, 0.0, 400.0, 300.0)
let panel = root.children[0]
let badge = panel.children[0]
let ul = root.children[3]
let para = root.children[len(root.children) - 1]
print(`badge at {int(badge.x)},{int(badge.y)} bg {badge.bg}; nudged {int(panel.children[1].x)},{int(panel.children[1].y)}; big {int(panel.children[2].rsize)}; corner {int(root.children[1].x)},{int(root.children[1].y)} {int(root.children[1].cw)}x{int(root.children[1].ch)}`)
print(`media keeps {root.children[2].text}; li gaps {int(ul.children[1].mt)} {int(ul.children[0].mt)}; li colours {ul.children[0].fg} {ul.children[1].fg} {ul.children[2].fg}; after {root.children[5].fg}; fallback {root.children[6].fg}`)
print(`checked opacity {root.children[7].alpha}; list scrolls {root.children[8].kind == UI_SCROLL} {int(root.children[8].ch)} of {int(root.children[8].content_h)}; para lines {len(para.lines)}`)
}
}