on master help sign in
Add .gitignore and remove build artifacts
commitdf8c888ee48e836733ea3f1ab31792313628f600 authoriko <mail@iko.soy> dateSun 15 Mar 2026, 02:48 (7 months ago)
12 files changed+1989
unifiedsplit
.gitignore+1 elm.json+24 examples/counter/elm.json+25 examples/counter/src/Main.elm+91 package.json+22 runtime/dom-shim.mjs+507 runtime/index.mjs+89 src/Lynx.elm+48 src/Lynx/Attributes.elm+158 src/Lynx/Element.elm+98 src/Lynx/Events.elm+258 src/Lynx/Style.elm+668
.gitignorenew file+1
@@ -0,0 +1 @@
1+elm-stuff/
elm.jsonnew file+24
@@ -0,0 +1,24 @@
1+{
2+ "type": "application",
3+ "source-directories": [
4+ "src"
5+ ],
6+ "elm-version": "0.19.1",
7+ "dependencies": {
8+ "direct": {
9+ "elm/browser": "1.0.2",
10+ "elm/core": "1.0.5",
11+ "elm/html": "1.0.0",
12+ "elm/json": "1.1.3"
13+ },
14+ "indirect": {
15+ "elm/time": "1.0.0",
16+ "elm/url": "1.0.0",
17+ "elm/virtual-dom": "1.0.3"
18+ }
19+ },
20+ "test-dependencies": {
21+ "direct": {},
22+ "indirect": {}
23+ }
24+}
examples/counter/elm.jsonnew file+25
@@ -0,0 +1,25 @@
1+{
2+ "type": "application",
3+ "source-directories": [
4+ "src",
5+ "../../src"
6+ ],
7+ "elm-version": "0.19.1",
8+ "dependencies": {
9+ "direct": {
10+ "elm/browser": "1.0.2",
11+ "elm/core": "1.0.5",
12+ "elm/html": "1.0.0",
13+ "elm/json": "1.1.3"
14+ },
15+ "indirect": {
16+ "elm/time": "1.0.0",
17+ "elm/url": "1.0.0",
18+ "elm/virtual-dom": "1.0.3"
19+ }
20+ },
21+ "test-dependencies": {
22+ "direct": {},
23+ "indirect": {}
24+ }
25+}
examples/counter/src/Main.elmnew file+91
@@ -0,0 +1,91 @@
1+module Main exposing (main)
2+
3+import Html exposing (Html)
4+import Lynx
5+import Lynx.Attributes as Attr
6+import Lynx.Element exposing (str, text, view)
7+import Lynx.Events exposing (onTap)
8+import Lynx.Style as Style exposing (px)
9+
10+
11+type alias Model =
12+ { count : Int
13+ }
14+
15+
16+type Msg
17+ = Increment
18+ | Decrement
19+
20+
21+main : Program () Model Msg
22+main =
23+ Lynx.sandbox
24+ { init = { count = 0 }
25+ , update = update
26+ , view = viewApp
27+ }
28+
29+
30+update : Msg -> Model -> Model
31+update msg model =
32+ case msg of
33+ Increment ->
34+ { model | count = model.count + 1 }
35+
36+ Decrement ->
37+ { model | count = model.count - 1 }
38+
39+
40+viewApp : Model -> Html Msg
41+viewApp model =
42+ view
43+ [ Attr.style
44+ [ Style.flexDirection Style.column
45+ , Style.alignItems Style.center
46+ , Style.justifyContent Style.center
47+ , Style.padding (px 20)
48+ ]
49+ ]
50+ [ text
51+ [ Attr.style
52+ [ Style.fontSize (px 32)
53+ , Style.fontWeight Style.bold
54+ , Style.marginBottom (px 20)
55+ ]
56+ ]
57+ [ str (String.fromInt model.count) ]
58+ , view
59+ [ Attr.style
60+ [ Style.flexDirection Style.row
61+ ]
62+ ]
63+ [ button Decrement "-"
64+ , button Increment "+"
65+ ]
66+ ]
67+
68+
69+button : Msg -> String -> Html Msg
70+button msg label =
71+ view
72+ [ onTap msg
73+ , Attr.style
74+ [ Style.width (px 60)
75+ , Style.height (px 60)
76+ , Style.backgroundColor "#4A90D9"
77+ , Style.borderRadius (px 8)
78+ , Style.alignItems Style.center
79+ , Style.justifyContent Style.center
80+ , Style.margin (px 10)
81+ ]
82+ ]
83+ [ text
84+ [ Attr.style
85+ [ Style.fontSize (px 28)
86+ , Style.color "#FFFFFF"
87+ , Style.fontWeight Style.bold
88+ ]
89+ ]
90+ [ str label ]
91+ ]
package.jsonnew file+22
@@ -0,0 +1,22 @@
1+{
2+ "name": "lynxjs-elm",
3+ "version": "0.1.0",
4+ "description": "Elm library for building LynxJS native apps",
5+ "type": "module",
6+ "exports": {
7+ "./runtime": "./runtime/index.mjs",
8+ "./runtime/dom-shim": "./runtime/dom-shim.mjs"
9+ },
10+ "files": [
11+ "src/",
12+ "runtime/",
13+ "elm.json"
14+ ],
15+ "keywords": [
16+ "elm",
17+ "lynxjs",
18+ "cross-platform",
19+ "native"
20+ ],
21+ "license": "MIT"
22+}
runtime/dom-shim.mjsnew file+507
@@ -0,0 +1,507 @@
1+/**
2+ * DOM Shim for LynxJS
3+ *
4+ * Provides fake DOM objects that translate standard DOM API calls
5+ * (createElement, appendChild, setAttribute, etc.) to LynxJS PAPI calls.
6+ * This allows Elm's compiled JS to work unmodified on LynxJS's PrimJS runtime.
7+ */
8+
9+// --- Helpers ---
10+
11+function camelToKebab(str) {
12+ return str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
13+}
14+
15+function kebabToCamel(str) {
16+ return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
17+}
18+
19+let nextNodeId = 1;
20+
21+// --- LynxTextNode ---
22+
23+export class LynxTextNode {
24+ constructor(text) {
25+ this.nodeType = 3;
26+ this.nodeValue = text;
27+ this.parentNode = null;
28+ this.nextSibling = null;
29+ this.previousSibling = null;
30+ this._papiElement = null;
31+ }
32+
33+ get textContent() {
34+ return this.nodeValue;
35+ }
36+
37+ set textContent(v) {
38+ this.nodeValue = v;
39+ this._syncText();
40+ }
41+
42+ /** Called by Elm's VirtualDom for text updates */
43+ replaceData(offset, length, data) {
44+ const before = this.nodeValue.substring(0, offset);
45+ const after = this.nodeValue.substring(offset + length);
46+ this.nodeValue = before + data + after;
47+ this._syncText();
48+ }
49+
50+ _syncText() {
51+ if (this._papiElement) {
52+ __SetAttribute(this._papiElement, "value", this.nodeValue);
53+ }
54+ }
55+
56+ _ensurePapi(parentPapi) {
57+ if (!this._papiElement) {
58+ this._papiElement = __CreateTextElement("text", this.nodeValue);
59+ if (parentPapi) {
60+ __AppendElement(parentPapi, this._papiElement);
61+ }
62+ }
63+ }
64+}
65+
66+// --- Style Proxy ---
67+
68+function createStyleProxy(node) {
69+ return new Proxy(
70+ {},
71+ {
72+ set(target, prop, value) {
73+ target[prop] = value;
74+ const cssProp = camelToKebab(prop);
75+ node._setStyleProperty(cssProp, value);
76+ return true;
77+ },
78+ get(target, prop) {
79+ if (prop === "cssText") {
80+ return node.getAttribute("style") || "";
81+ }
82+ if (prop === "setProperty") {
83+ return (p, v) => {
84+ target[kebabToCamel(p)] = v;
85+ node._setStyleProperty(p, v);
86+ };
87+ }
88+ if (prop === "removeProperty") {
89+ return (p) => {
90+ const camel = kebabToCamel(p);
91+ const old = target[camel];
92+ delete target[camel];
93+ node._setStyleProperty(p, "");
94+ return old || "";
95+ };
96+ }
97+ if (prop === "getPropertyValue") {
98+ return (p) => target[kebabToCamel(p)] || "";
99+ }
100+ return target[prop];
101+ },
102+ }
103+ );
104+}
105+
106+// --- LynxNode ---
107+
108+export class LynxNode {
109+ constructor(tagName) {
110+ this.nodeType = 1;
111+ this.tagName = tagName.toUpperCase();
112+ this.nodeName = this.tagName;
113+ this._tagLower = tagName.toLowerCase();
114+ this._id = nextNodeId++;
115+ this._papiElement = null;
116+ this._attrs = {};
117+ this._children = [];
118+ this._eventListeners = {};
119+ this._styleMap = {};
120+ this.parentNode = null;
121+ this.nextSibling = null;
122+ this.previousSibling = null;
123+ this.style = createStyleProxy(this);
124+ }
125+
126+ // --- Child access ---
127+
128+ get childNodes() {
129+ return this._children;
130+ }
131+
132+ get firstChild() {
133+ return this._children[0] || null;
134+ }
135+
136+ get lastChild() {
137+ return this._children[this._children.length - 1] || null;
138+ }
139+
140+ // --- className ---
141+
142+ get className() {
143+ return this._attrs["class"] || "";
144+ }
145+
146+ set className(v) {
147+ this.setAttribute("class", v);
148+ }
149+
150+ // --- namespaceURI (Elm checks this) ---
151+
152+ get namespaceURI() {
153+ return "http://www.w3.org/1999/xhtml";
154+ }
155+
156+ // --- Attribute methods ---
157+
158+ setAttribute(name, value) {
159+ this._attrs[name] = String(value);
160+ if (name === "style") {
161+ this._parseAndApplyStyleString(value);
162+ } else if (this._papiElement) {
163+ __SetAttribute(this._papiElement, name, String(value));
164+ }
165+ }
166+
167+ getAttribute(name) {
168+ return this._attrs[name] !== undefined ? this._attrs[name] : null;
169+ }
170+
171+ removeAttribute(name) {
172+ delete this._attrs[name];
173+ if (this._papiElement) {
174+ __SetAttribute(this._papiElement, name, "");
175+ }
176+ }
177+
178+ // --- Style handling ---
179+
180+ _parseAndApplyStyleString(cssText) {
181+ if (!this._papiElement) return;
182+ const pairs = cssText.split(";");
183+ for (const pair of pairs) {
184+ const colon = pair.indexOf(":");
185+ if (colon === -1) continue;
186+ const prop = pair.substring(0, colon).trim();
187+ const val = pair.substring(colon + 1).trim();
188+ if (prop) {
189+ this._setStyleProperty(prop, val);
190+ }
191+ }
192+ }
193+
194+ _setStyleProperty(prop, value) {
195+ this._styleMap[prop] = value;
196+ if (this._papiElement) {
197+ __SetAttribute(this._papiElement, prop, value);
198+ }
199+ }
200+
201+ // --- Child mutation ---
202+
203+ appendChild(child) {
204+ if (child.nodeType === 11) {
205+ // DocumentFragment
206+ const kids = child._children.slice();
207+ for (const k of kids) {
208+ this.appendChild(k);
209+ }
210+ return child;
211+ }
212+
213+ this._removeFromParent(child);
214+ this._children.push(child);
215+ child.parentNode = this;
216+ this._updateSiblings();
217+
218+ if (this._papiElement) {
219+ this._attachChild(child);
220+ }
221+
222+ return child;
223+ }
224+
225+ insertBefore(newChild, refChild) {
226+ if (newChild.nodeType === 11) {
227+ const kids = newChild._children.slice();
228+ for (const k of kids) {
229+ this.insertBefore(k, refChild);
230+ }
231+ return newChild;
232+ }
233+
234+ this._removeFromParent(newChild);
235+
236+ if (!refChild) {
237+ return this.appendChild(newChild);
238+ }
239+
240+ const idx = this._children.indexOf(refChild);
241+ if (idx === -1) {
242+ return this.appendChild(newChild);
243+ }
244+
245+ this._children.splice(idx, 0, newChild);
246+ newChild.parentNode = this;
247+ this._updateSiblings();
248+
249+ if (this._papiElement) {
250+ this._attachChildBefore(newChild, refChild);
251+ }
252+
253+ return newChild;
254+ }
255+
256+ removeChild(child) {
257+ const idx = this._children.indexOf(child);
258+ if (idx === -1) return child;
259+
260+ this._children.splice(idx, 1);
261+ child.parentNode = null;
262+ this._updateSiblings();
263+
264+ if (this._papiElement && child._papiElement) {
265+ __RemoveElement(this._papiElement, child._papiElement);
266+ }
267+
268+ return child;
269+ }
270+
271+ replaceChild(newChild, oldChild) {
272+ if (newChild.nodeType === 11) {
273+ const kids = newChild._children.slice();
274+ const idx = this._children.indexOf(oldChild);
275+ if (idx === -1) return oldChild;
276+
277+ // Insert all fragment children in place of oldChild
278+ this._children.splice(idx, 1, ...kids);
279+ for (const k of kids) {
280+ k.parentNode = this;
281+ }
282+ oldChild.parentNode = null;
283+ this._updateSiblings();
284+
285+ if (this._papiElement) {
286+ for (const k of kids) {
287+ this._attachChildBefore(k, oldChild.nextSibling);
288+ }
289+ if (oldChild._papiElement) {
290+ __RemoveElement(this._papiElement, oldChild._papiElement);
291+ }
292+ }
293+
294+ return oldChild;
295+ }
296+
297+ const idx = this._children.indexOf(oldChild);
298+ if (idx === -1) return oldChild;
299+
300+ this._removeFromParent(newChild);
301+ this._children[idx] = newChild;
302+ newChild.parentNode = this;
303+ oldChild.parentNode = null;
304+ this._updateSiblings();
305+
306+ if (this._papiElement) {
307+ this._attachChildBefore(newChild, oldChild.nextSibling);
308+ if (oldChild._papiElement) {
309+ __RemoveElement(this._papiElement, oldChild._papiElement);
310+ }
311+ }
312+
313+ return oldChild;
314+ }
315+
316+ // --- Events ---
317+
318+ addEventListener(type, handler, options) {
319+ if (!this._eventListeners[type]) {
320+ this._eventListeners[type] = [];
321+ }
322+ this._eventListeners[type].push(handler);
323+
324+ if (this._papiElement) {
325+ __AddEvent(this._papiElement, "bind", type, (papiEvent) => {
326+ const domEvent = {
327+ type,
328+ target: this,
329+ currentTarget: this,
330+ detail: papiEvent?.detail || papiEvent || {},
331+ preventDefault() {},
332+ stopPropagation() {},
333+ };
334+ handler(domEvent);
335+ });
336+ }
337+ }
338+
339+ removeEventListener(type, handler, options) {
340+ const listeners = this._eventListeners[type];
341+ if (!listeners) return;
342+ const idx = listeners.indexOf(handler);
343+ if (idx !== -1) {
344+ listeners.splice(idx, 1);
345+ }
346+ if (this._papiElement) {
347+ __RemoveEvent(this._papiElement, "bind", type);
348+ }
349+ }
350+
351+ // --- Internal helpers ---
352+
353+ _removeFromParent(child) {
354+ if (child.parentNode) {
355+ child.parentNode.removeChild(child);
356+ }
357+ }
358+
359+ _updateSiblings() {
360+ for (let i = 0; i < this._children.length; i++) {
361+ const child = this._children[i];
362+ child.previousSibling = this._children[i - 1] || null;
363+ child.nextSibling = this._children[i + 1] || null;
364+ }
365+ }
366+
367+ _ensurePapi() {
368+ if (!this._papiElement) {
369+ this._papiElement = __CreateElement(this._tagLower);
370+
371+ // Apply buffered attributes
372+ for (const [k, v] of Object.entries(this._attrs)) {
373+ if (k === "style") {
374+ this._parseAndApplyStyleString(v);
375+ } else {
376+ __SetAttribute(this._papiElement, k, v);
377+ }
378+ }
379+
380+ // Apply buffered style properties
381+ for (const [prop, val] of Object.entries(this._styleMap)) {
382+ __SetAttribute(this._papiElement, prop, val);
383+ }
384+
385+ // Re-bind events
386+ for (const [type, handlers] of Object.entries(this._eventListeners)) {
387+ for (const handler of handlers) {
388+ __AddEvent(this._papiElement, "bind", type, (papiEvent) => {
389+ const domEvent = {
390+ type,
391+ target: this,
392+ currentTarget: this,
393+ detail: papiEvent?.detail || papiEvent || {},
394+ preventDefault() {},
395+ stopPropagation() {},
396+ };
397+ handler(domEvent);
398+ });
399+ }
400+ }
401+ }
402+ }
403+
404+ _attachChild(child) {
405+ if (child.nodeType === 3) {
406+ child._ensurePapi(this._papiElement);
407+ } else {
408+ child._ensurePapi();
409+ if (child._papiElement) {
410+ __AppendElement(this._papiElement, child._papiElement);
411+ }
412+ }
413+ // Recursively attach grandchildren
414+ if (child._children) {
415+ for (const grandchild of child._children) {
416+ child._attachChild(grandchild);
417+ }
418+ }
419+ }
420+
421+ _attachChildBefore(newChild, refChild) {
422+ if (newChild.nodeType === 3) {
423+ newChild._ensurePapi(this._papiElement);
424+ } else {
425+ newChild._ensurePapi();
426+ }
427+
428+ if (!newChild._papiElement) return;
429+
430+ if (refChild && refChild._papiElement) {
431+ __InsertBefore(this._papiElement, newChild._papiElement, refChild._papiElement);
432+ } else {
433+ __AppendElement(this._papiElement, newChild._papiElement);
434+ }
435+
436+ // Recursively attach grandchildren
437+ if (newChild._children) {
438+ for (const grandchild of newChild._children) {
439+ newChild._attachChild(grandchild);
440+ }
441+ }
442+ }
443+}
444+
445+// --- LynxDocumentFragment ---
446+
447+export class LynxDocumentFragment {
448+ constructor() {
449+ this.nodeType = 11;
450+ this._children = [];
451+ this.parentNode = null;
452+ this.nextSibling = null;
453+ this.previousSibling = null;
454+ }
455+
456+ get childNodes() {
457+ return this._children;
458+ }
459+
460+ get firstChild() {
461+ return this._children[0] || null;
462+ }
463+
464+ appendChild(child) {
465+ if (child.parentNode) {
466+ child.parentNode.removeChild(child);
467+ }
468+ this._children.push(child);
469+ child.parentNode = this;
470+ return child;
471+ }
472+}
473+
474+// --- LynxDocument ---
475+
476+export class LynxDocument {
477+ constructor() {
478+ this.body = new LynxNode("body");
479+ this.body._papiElement = __CreatePage("page", 0);
480+ this._nextId = 1;
481+ }
482+
483+ createElement(tagName) {
484+ return new LynxNode(tagName);
485+ }
486+
487+ createTextNode(text) {
488+ return new LynxTextNode(text);
489+ }
490+
491+ createDocumentFragment() {
492+ return new LynxDocumentFragment();
493+ }
494+
495+ createComment(_text) {
496+ // Comments are no-ops in LynxJS; return a minimal object
497+ return new LynxTextNode("");
498+ }
499+
500+ getElementById(_id) {
501+ return null;
502+ }
503+
504+ createElementNS(_ns, tagName) {
505+ return this.createElement(tagName);
506+ }
507+}
runtime/index.mjsnew file+89
@@ -0,0 +1,89 @@
1+/**
2+ * Bootstrap for LynxJS Elm apps.
3+ *
4+ * Installs DOM shim globals (document, window, navigator, requestAnimationFrame)
5+ * so that Elm's compiled JS works on PrimJS without modification.
6+ */
7+
8+import { LynxDocument } from "./dom-shim.mjs";
9+
10+// --- Install globals ---
11+
12+const doc = new LynxDocument();
13+
14+globalThis.document = doc;
15+
16+globalThis.window = globalThis;
17+
18+globalThis.navigator = {
19+ userAgent: "LynxJS/Elm",
20+};
21+
22+// --- requestAnimationFrame polyfill ---
23+
24+const _rafQueue = [];
25+let _rafId = 0;
26+
27+globalThis.requestAnimationFrame = function (callback) {
28+ const id = ++_rafId;
29+ _rafQueue.push({ id, callback });
30+
31+ // Schedule a microtask to flush
32+ Promise.resolve().then(flushRAF);
33+
34+ return id;
35+};
36+
37+globalThis.cancelAnimationFrame = function (id) {
38+ const idx = _rafQueue.findIndex((entry) => entry.id === id);
39+ if (idx !== -1) {
40+ _rafQueue.splice(idx, 1);
41+ }
42+};
43+
44+function flushRAF() {
45+ // Process all queued callbacks
46+ while (_rafQueue.length > 0) {
47+ const entry = _rafQueue.shift();
48+ try {
49+ entry.callback(Date.now());
50+ } catch (e) {
51+ console.error("rAF callback error:", e);
52+ }
53+ }
54+
55+ // Flush PAPI element tree after all mutations
56+ if (typeof __FlushElementTree === "function") {
57+ __FlushElementTree();
58+ }
59+}
60+
61+// --- Elm app initialization helper ---
62+
63+/**
64+ * Initialize an Elm app on the LynxJS shim document.
65+ *
66+ * @param {object} elmModule - The Elm module (e.g., `Elm.Main`)
67+ * @param {object} [flags] - Flags to pass to the Elm app
68+ * @returns {object} The Elm app instance
69+ *
70+ * @example
71+ * import { initElmApp } from "lynxjs-elm/runtime";
72+ * import { Elm } from "./elm.js";
73+ * const app = initElmApp(Elm.Main, { startCount: 0 });
74+ */
75+export function initElmApp(elmModule, flags) {
76+ const app = elmModule.init({
77+ node: document.body,
78+ flags: flags,
79+ });
80+
81+ // Flush after initial render
82+ if (typeof __FlushElementTree === "function") {
83+ __FlushElementTree();
84+ }
85+
86+ return app;
87+}
88+
89+export { doc as document };
src/Lynx.elmnew file+48
@@ -0,0 +1,48 @@
1+module Lynx exposing (sandbox, element, document)
2+
3+{-| Entry points for LynxJS apps using The Elm Architecture.
4+
5+@docs sandbox, element, document
6+
7+-}
8+
9+import Browser
10+import Html exposing (Html)
11+
12+
13+{-| Create a simple LynxJS app with no commands or subscriptions.
14+-}
15+sandbox :
16+ { init : model
17+ , view : model -> Html msg
18+ , update : msg -> model -> model
19+ }
20+ -> Program () model msg
21+sandbox =
22+ Browser.sandbox
23+
24+
25+{-| Create a LynxJS app with commands and subscriptions.
26+-}
27+element :
28+ { init : flags -> ( model, Cmd msg )
29+ , view : model -> Html msg
30+ , update : msg -> model -> ( model, Cmd msg )
31+ , subscriptions : model -> Sub msg
32+ }
33+ -> Program flags model msg
34+element =
35+ Browser.element
36+
37+
38+{-| Create a full LynxJS document app.
39+-}
40+document :
41+ { init : flags -> ( model, Cmd msg )
42+ , view : model -> Browser.Document msg
43+ , update : msg -> model -> ( model, Cmd msg )
44+ , subscriptions : model -> Sub msg
45+ }
46+ -> Program flags model msg
47+document =
48+ Browser.document
src/Lynx/Attributes.elmnew file+158
@@ -0,0 +1,158 @@
1+module Lynx.Attributes exposing
2+ ( style
3+ , src, placeholder, value, defaultValue
4+ , scrollOrientation, listType, spanCount, mode
5+ , textMaxline, maxlength, autoFocus, disabled, readOnly
6+ , accessibilityLabel, accessibilityRole, accessibilityHint
7+ , attribute
8+ )
9+
10+{-| Typed attribute constructors for LynxJS elements.
11+
12+@docs style
13+@docs src, placeholder, value, defaultValue
14+@docs scrollOrientation, listType, spanCount, mode
15+@docs textMaxline, maxlength, autoFocus, disabled, readOnly
16+@docs accessibilityLabel, accessibilityRole, accessibilityHint
17+@docs attribute
18+
19+-}
20+
21+import Html
22+import Html.Attributes
23+import Lynx.Style as Style
24+
25+
26+{-| Apply a list of styles as a single CSS string attribute.
27+-}
28+style : List Style.Style -> Html.Attribute msg
29+style =
30+ Style.toAttribute
31+
32+
33+{-| Image source URL.
34+-}
35+src : String -> Html.Attribute msg
36+src =
37+ Html.Attributes.attribute "src"
38+
39+
40+{-| Placeholder text for input/textarea.
41+-}
42+placeholder : String -> Html.Attribute msg
43+placeholder =
44+ Html.Attributes.attribute "placeholder"
45+
46+
47+{-| Current value for input/textarea.
48+-}
49+value : String -> Html.Attribute msg
50+value =
51+ Html.Attributes.attribute "value"
52+
53+
54+{-| Default value for input/textarea.
55+-}
56+defaultValue : String -> Html.Attribute msg
57+defaultValue =
58+ Html.Attributes.attribute "default-value"
59+
60+
61+{-| Scroll orientation: "vertical" or "horizontal".
62+-}
63+scrollOrientation : String -> Html.Attribute msg
64+scrollOrientation =
65+ Html.Attributes.attribute "scroll-orientation"
66+
67+
68+{-| List layout type: "flow", "waterfall", or "grid".
69+-}
70+listType : String -> Html.Attribute msg
71+listType =
72+ Html.Attributes.attribute "list-type"
73+
74+
75+{-| Number of columns for grid/waterfall list.
76+-}
77+spanCount : Int -> Html.Attribute msg
78+spanCount n =
79+ Html.Attributes.attribute "span-count" (String.fromInt n)
80+
81+
82+{-| Input mode: "text", "password", "number", etc.
83+-}
84+mode : String -> Html.Attribute msg
85+mode =
86+ Html.Attributes.attribute "mode"
87+
88+
89+{-| Maximum number of lines for text elements.
90+-}
91+textMaxline : Int -> Html.Attribute msg
92+textMaxline n =
93+ Html.Attributes.attribute "text-maxline" (String.fromInt n)
94+
95+
96+{-| Maximum character length for input/textarea.
97+-}
98+maxlength : Int -> Html.Attribute msg
99+maxlength n =
100+ Html.Attributes.attribute "maxlength" (String.fromInt n)
101+
102+
103+{-| Auto-focus the input on mount.
104+-}
105+autoFocus : Bool -> Html.Attribute msg
106+autoFocus b =
107+ Html.Attributes.attribute "auto-focus" (boolToString b)
108+
109+
110+{-| Disable interaction.
111+-}
112+disabled : Bool -> Html.Attribute msg
113+disabled b =
114+ Html.Attributes.attribute "disabled" (boolToString b)
115+
116+
117+{-| Read-only input.
118+-}
119+readOnly : Bool -> Html.Attribute msg
120+readOnly b =
121+ Html.Attributes.attribute "read-only" (boolToString b)
122+
123+
124+{-| Accessibility label for screen readers.
125+-}
126+accessibilityLabel : String -> Html.Attribute msg
127+accessibilityLabel =
128+ Html.Attributes.attribute "accessibility-label"
129+
130+
131+{-| Accessibility role.
132+-}
133+accessibilityRole : String -> Html.Attribute msg
134+accessibilityRole =
135+ Html.Attributes.attribute "accessibility-role"
136+
137+
138+{-| Accessibility hint.
139+-}
140+accessibilityHint : String -> Html.Attribute msg
141+accessibilityHint =
142+ Html.Attributes.attribute "accessibility-hint"
143+
144+
145+{-| Escape hatch for arbitrary attributes.
146+-}
147+attribute : String -> String -> Html.Attribute msg
148+attribute =
149+ Html.Attributes.attribute
150+
151+
152+boolToString : Bool -> String
153+boolToString b =
154+ if b then
155+ "true"
156+
157+ else
158+ "false"
src/Lynx/Element.elmnew file+98
@@ -0,0 +1,98 @@
1+module Lynx.Element exposing
2+ ( view, text, image
3+ , scrollView, list, listItem
4+ , input, textarea
5+ , page
6+ , str, node
7+ )
8+
9+{-| Element constructors for LynxJS built-in components.
10+
11+Each function wraps `Html.node` with the corresponding LynxJS tag name.
12+
13+@docs view, text, image
14+@docs scrollView, list, listItem
15+@docs input, textarea
16+@docs page
17+@docs str, node
18+
19+-}
20+
21+import Html exposing (Html)
22+
23+
24+{-| A generic container element. Maps to `<view>`.
25+-}
26+view : List (Html.Attribute msg) -> List (Html msg) -> Html msg
27+view =
28+ Html.node "view"
29+
30+
31+{-| A text container element. Maps to `<text>`.
32+-}
33+text : List (Html.Attribute msg) -> List (Html msg) -> Html msg
34+text =
35+ Html.node "text"
36+
37+
38+{-| An image element. Maps to `<image>`.
39+-}
40+image : List (Html.Attribute msg) -> List (Html msg) -> Html msg
41+image =
42+ Html.node "image"
43+
44+
45+{-| A scrollable container. Maps to `<scroll-view>`.
46+-}
47+scrollView : List (Html.Attribute msg) -> List (Html msg) -> Html msg
48+scrollView =
49+ Html.node "scroll-view"
50+
51+
52+{-| A recycler list container. Maps to `<list>`.
53+-}
54+list : List (Html.Attribute msg) -> List (Html msg) -> Html msg
55+list =
56+ Html.node "list"
57+
58+
59+{-| A list item within a `list`. Maps to `<list-item>`.
60+-}
61+listItem : List (Html.Attribute msg) -> List (Html msg) -> Html msg
62+listItem =
63+ Html.node "list-item"
64+
65+
66+{-| A text input field. Maps to `<input>`.
67+-}
68+input : List (Html.Attribute msg) -> List (Html msg) -> Html msg
69+input =
70+ Html.node "input"
71+
72+
73+{-| A multi-line text input. Maps to `<textarea>`.
74+-}
75+textarea : List (Html.Attribute msg) -> List (Html msg) -> Html msg
76+textarea =
77+ Html.node "textarea"
78+
79+
80+{-| The root page element. Maps to `<page>`.
81+-}
82+page : List (Html.Attribute msg) -> List (Html msg) -> Html msg
83+page =
84+ Html.node "page"
85+
86+
87+{-| A plain text string. Alias for `Html.text`.
88+-}
89+str : String -> Html msg
90+str =
91+ Html.text
92+
93+
94+{-| Escape hatch: create an element with any tag name.
95+-}
96+node : String -> List (Html.Attribute msg) -> List (Html msg) -> Html msg
97+node =
98+ Html.node
src/Lynx/Events.elmnew file+258
@@ -0,0 +1,258 @@
1+module Lynx.Events exposing
2+ ( onTap, onLongPress
3+ , onTouchStart, onTouchMove, onTouchEnd
4+ , onInput, onFocus, onBlur
5+ , onScroll, onScrollToUpper, onScrollToLower
6+ , onAppear, onDisappear
7+ , onLayoutChange
8+ , onLoad, onError
9+ , TouchInfo, ScrollInfo, LayoutInfo
10+ , touchInfoDecoder, scrollInfoDecoder, layoutInfoDecoder
11+ )
12+
13+{-| Event handlers for LynxJS elements.
14+
15+
16+# Tap Events
17+
18+@docs onTap, onLongPress
19+
20+
21+# Touch Events
22+
23+@docs onTouchStart, onTouchMove, onTouchEnd
24+
25+
26+# Input Events
27+
28+@docs onInput, onFocus, onBlur
29+
30+
31+# Scroll Events
32+
33+@docs onScroll, onScrollToUpper, onScrollToLower
34+
35+
36+# Visibility Events
37+
38+@docs onAppear, onDisappear
39+
40+
41+# Layout Events
42+
43+@docs onLayoutChange
44+
45+
46+# Loading Events
47+
48+@docs onLoad, onError
49+
50+
51+# Decoder Types
52+
53+@docs TouchInfo, ScrollInfo, LayoutInfo
54+@docs touchInfoDecoder, scrollInfoDecoder, layoutInfoDecoder
55+
56+-}
57+
58+import Html
59+import Html.Events
60+import Json.Decode as Decode
61+
62+
63+{-| Touch event data.
64+-}
65+type alias TouchInfo =
66+ { x : Float
67+ , y : Float
68+ }
69+
70+
71+{-| Scroll event data.
72+-}
73+type alias ScrollInfo =
74+ { scrollTop : Float
75+ , scrollLeft : Float
76+ }
77+
78+
79+{-| Layout change event data.
80+-}
81+type alias LayoutInfo =
82+ { width : Float
83+ , height : Float
84+ , x : Float
85+ , y : Float
86+ }
87+
88+
89+{-| Decode touch info from event detail.
90+-}
91+touchInfoDecoder : Decode.Decoder TouchInfo
92+touchInfoDecoder =
93+ Decode.map2 TouchInfo
94+ (Decode.at [ "detail", "x" ] Decode.float)
95+ (Decode.at [ "detail", "y" ] Decode.float)
96+
97+
98+{-| Decode scroll info from event detail.
99+-}
100+scrollInfoDecoder : Decode.Decoder ScrollInfo
101+scrollInfoDecoder =
102+ Decode.map2 ScrollInfo
103+ (Decode.at [ "detail", "scrollTop" ] Decode.float)
104+ (Decode.at [ "detail", "scrollLeft" ] Decode.float)
105+
106+
107+{-| Decode layout info from event detail.
108+-}
109+layoutInfoDecoder : Decode.Decoder LayoutInfo
110+layoutInfoDecoder =
111+ Decode.map4 LayoutInfo
112+ (Decode.at [ "detail", "width" ] Decode.float)
113+ (Decode.at [ "detail", "height" ] Decode.float)
114+ (Decode.at [ "detail", "x" ] Decode.float)
115+ (Decode.at [ "detail", "y" ] Decode.float)
116+
117+
118+
119+-- TAP EVENTS
120+
121+
122+{-| Handle tap (click) events.
123+-}
124+onTap : msg -> Html.Attribute msg
125+onTap msg =
126+ Html.Events.on "tap" (Decode.succeed msg)
127+
128+
129+{-| Handle long press events.
130+-}
131+onLongPress : msg -> Html.Attribute msg
132+onLongPress msg =
133+ Html.Events.on "longpress" (Decode.succeed msg)
134+
135+
136+
137+-- TOUCH EVENTS
138+
139+
140+{-| Handle touch start events.
141+-}
142+onTouchStart : (TouchInfo -> msg) -> Html.Attribute msg
143+onTouchStart toMsg =
144+ Html.Events.on "touchstart" (Decode.map toMsg touchInfoDecoder)
145+
146+
147+{-| Handle touch move events.
148+-}
149+onTouchMove : (TouchInfo -> msg) -> Html.Attribute msg
150+onTouchMove toMsg =
151+ Html.Events.on "touchmove" (Decode.map toMsg touchInfoDecoder)
152+
153+
154+{-| Handle touch end events.
155+-}
156+onTouchEnd : (TouchInfo -> msg) -> Html.Attribute msg
157+onTouchEnd toMsg =
158+ Html.Events.on "touchend" (Decode.map toMsg touchInfoDecoder)
159+
160+
161+
162+-- INPUT EVENTS
163+
164+
165+{-| Handle text input events.
166+-}
167+onInput : (String -> msg) -> Html.Attribute msg
168+onInput toMsg =
169+ Html.Events.on "input"
170+ (Decode.map toMsg
171+ (Decode.at [ "detail", "value" ] Decode.string)
172+ )
173+
174+
175+{-| Handle focus events.
176+-}
177+onFocus : msg -> Html.Attribute msg
178+onFocus msg =
179+ Html.Events.on "focus" (Decode.succeed msg)
180+
181+
182+{-| Handle blur events.
183+-}
184+onBlur : msg -> Html.Attribute msg
185+onBlur msg =
186+ Html.Events.on "blur" (Decode.succeed msg)
187+
188+
189+
190+-- SCROLL EVENTS
191+
192+
193+{-| Handle scroll events.
194+-}
195+onScroll : (ScrollInfo -> msg) -> Html.Attribute msg
196+onScroll toMsg =
197+ Html.Events.on "scroll" (Decode.map toMsg scrollInfoDecoder)
198+
199+
200+{-| Handle scroll-to-upper-boundary events.
201+-}
202+onScrollToUpper : msg -> Html.Attribute msg
203+onScrollToUpper msg =
204+ Html.Events.on "scrolltoupper" (Decode.succeed msg)
205+
206+
207+{-| Handle scroll-to-lower-boundary events.
208+-}
209+onScrollToLower : msg -> Html.Attribute msg
210+onScrollToLower msg =
211+ Html.Events.on "scrolltolower" (Decode.succeed msg)
212+
213+
214+
215+-- VISIBILITY EVENTS
216+
217+
218+{-| Handle element appearing in viewport.
219+-}
220+onAppear : msg -> Html.Attribute msg
221+onAppear msg =
222+ Html.Events.on "appear" (Decode.succeed msg)
223+
224+
225+{-| Handle element disappearing from viewport.
226+-}
227+onDisappear : msg -> Html.Attribute msg
228+onDisappear msg =
229+ Html.Events.on "disappear" (Decode.succeed msg)
230+
231+
232+
233+-- LAYOUT EVENTS
234+
235+
236+{-| Handle layout change events.
237+-}
238+onLayoutChange : (LayoutInfo -> msg) -> Html.Attribute msg
239+onLayoutChange toMsg =
240+ Html.Events.on "layoutchange" (Decode.map toMsg layoutInfoDecoder)
241+
242+
243+
244+-- LOADING EVENTS
245+
246+
247+{-| Handle successful load events (e.g., image loaded).
248+-}
249+onLoad : msg -> Html.Attribute msg
250+onLoad msg =
251+ Html.Events.on "load" (Decode.succeed msg)
252+
253+
254+{-| Handle error events (e.g., image failed to load).
255+-}
256+onError : msg -> Html.Attribute msg
257+onError msg =
258+ Html.Events.on "error" (Decode.succeed msg)
src/Lynx/Style.elmnew file+668
@@ -0,0 +1,668 @@
1+module Lynx.Style exposing
2+ ( Style
3+ , toString
4+ , toAttribute
5+ , px, pct, rpx, vh, vw
6+ , width, height, minWidth, minHeight, maxWidth, maxHeight
7+ , padding, paddingTop, paddingRight, paddingBottom, paddingLeft
8+ , margin, marginTop, marginRight, marginBottom, marginLeft
9+ , borderWidth, borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidth
10+ , borderRadius, borderTopLeftRadius, borderTopRightRadius, borderBottomLeftRadius, borderBottomRightRadius
11+ , borderColor, borderStyle
12+ , top, right, bottom, left
13+ , flex, flexGrow, flexShrink, flexBasis
14+ , flexDirection, flexWrap, justifyContent, alignItems, alignSelf, alignContent
15+ , position, display, overflow
16+ , opacity, zIndex
17+ , backgroundColor, color
18+ , fontSize, fontWeight, fontFamily, fontStyle
19+ , lineHeight, letterSpacing, textAlign, textDecoration, textOverflow
20+ , whiteSpace, wordBreak
21+ , row, column, rowReverse, columnReverse
22+ , wrap, nowrap
23+ , flexStart, flexEnd, center, spaceBetween, spaceAround, spaceEvenly
24+ , stretch, baseline
25+ , relative, absolute, fixed
26+ , visible, hidden, scroll
27+ , bold, normal, italic
28+ , underline, lineThrough
29+ , ellipsis, clip
30+ , solid, dashed, dotted
31+ , property
32+ )
33+
34+{-| Typed CSS style helpers for LynxJS elements.
35+
36+
37+# Style Type
38+
39+@docs Style, toString, toAttribute
40+
41+
42+# Units
43+
44+@docs px, pct, rpx, vh, vw
45+
46+
47+# Sizing
48+
49+@docs width, height, minWidth, minHeight, maxWidth, maxHeight
50+
51+
52+# Spacing
53+
54+@docs padding, paddingTop, paddingRight, paddingBottom, paddingLeft
55+@docs margin, marginTop, marginRight, marginBottom, marginLeft
56+
57+
58+# Borders
59+
60+@docs borderWidth, borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidth
61+@docs borderRadius, borderTopLeftRadius, borderTopRightRadius, borderBottomLeftRadius, borderBottomRightRadius
62+@docs borderColor, borderStyle
63+
64+
65+# Position Offsets
66+
67+@docs top, right, bottom, left
68+
69+
70+# Flexbox
71+
72+@docs flex, flexGrow, flexShrink, flexBasis
73+@docs flexDirection, flexWrap, justifyContent, alignItems, alignSelf, alignContent
74+
75+
76+# Layout
77+
78+@docs position, display, overflow
79+
80+
81+# Visual
82+
83+@docs opacity, zIndex, backgroundColor, color
84+
85+
86+# Typography
87+
88+@docs fontSize, fontWeight, fontFamily, fontStyle
89+@docs lineHeight, letterSpacing, textAlign, textDecoration, textOverflow
90+@docs whiteSpace, wordBreak
91+
92+
93+# Value Constants
94+
95+@docs row, column, rowReverse, columnReverse
96+@docs wrap, nowrap
97+@docs flexStart, flexEnd, center, spaceBetween, spaceAround, spaceEvenly
98+@docs stretch, baseline
99+@docs relative, absolute, fixed
100+@docs visible, hidden, scroll
101+@docs bold, normal, italic
102+@docs underline, lineThrough
103+@docs ellipsis, clip
104+@docs solid, dashed, dotted
105+
106+
107+# Escape Hatch
108+
109+@docs property
110+
111+-}
112+
113+import Html
114+import Html.Attributes
115+
116+
117+{-| A single CSS property-value pair.
118+-}
119+type Style
120+ = Style String String
121+
122+
123+{-| Render a list of styles to a CSS string.
124+-}
125+toString : List Style -> String
126+toString styles =
127+ styles
128+ |> List.map (\(Style k v) -> k ++ ": " ++ v)
129+ |> String.join "; "
130+
131+
132+{-| Convert styles to an `Html.Attribute`.
133+-}
134+toAttribute : List Style -> Html.Attribute msg
135+toAttribute styles =
136+ Html.Attributes.attribute "style" (toString styles)
137+
138+
139+
140+-- UNITS
141+
142+
143+{-| Pixels.
144+-}
145+px : Float -> String
146+px n =
147+ String.fromFloat n ++ "px"
148+
149+
150+{-| Percentage.
151+-}
152+pct : Float -> String
153+pct n =
154+ String.fromFloat n ++ "%"
155+
156+
157+{-| Responsive pixels (LynxJS unit).
158+-}
159+rpx : Float -> String
160+rpx n =
161+ String.fromFloat n ++ "rpx"
162+
163+
164+{-| Viewport height.
165+-}
166+vh : Float -> String
167+vh n =
168+ String.fromFloat n ++ "vh"
169+
170+
171+{-| Viewport width.
172+-}
173+vw : Float -> String
174+vw n =
175+ String.fromFloat n ++ "vw"
176+
177+
178+
179+-- SIZING
180+
181+
182+width : String -> Style
183+width =
184+ Style "width"
185+
186+
187+height : String -> Style
188+height =
189+ Style "height"
190+
191+
192+minWidth : String -> Style
193+minWidth =
194+ Style "min-width"
195+
196+
197+minHeight : String -> Style
198+minHeight =
199+ Style "min-height"
200+
201+
202+maxWidth : String -> Style
203+maxWidth =
204+ Style "max-width"
205+
206+
207+maxHeight : String -> Style
208+maxHeight =
209+ Style "max-height"
210+
211+
212+
213+-- SPACING
214+
215+
216+padding : String -> Style
217+padding =
218+ Style "padding"
219+
220+
221+paddingTop : String -> Style
222+paddingTop =
223+ Style "padding-top"
224+
225+
226+paddingRight : String -> Style
227+paddingRight =
228+ Style "padding-right"
229+
230+
231+paddingBottom : String -> Style
232+paddingBottom =
233+ Style "padding-bottom"
234+
235+
236+paddingLeft : String -> Style
237+paddingLeft =
238+ Style "padding-left"
239+
240+
241+margin : String -> Style
242+margin =
243+ Style "margin"
244+
245+
246+marginTop : String -> Style
247+marginTop =
248+ Style "margin-top"
249+
250+
251+marginRight : String -> Style
252+marginRight =
253+ Style "margin-right"
254+
255+
256+marginBottom : String -> Style
257+marginBottom =
258+ Style "margin-bottom"
259+
260+
261+marginLeft : String -> Style
262+marginLeft =
263+ Style "margin-left"
264+
265+
266+
267+-- BORDERS
268+
269+
270+borderWidth : String -> Style
271+borderWidth =
272+ Style "border-width"
273+
274+
275+borderTopWidth : String -> Style
276+borderTopWidth =
277+ Style "border-top-width"
278+
279+
280+borderRightWidth : String -> Style
281+borderRightWidth =
282+ Style "border-right-width"
283+
284+
285+borderBottomWidth : String -> Style
286+borderBottomWidth =
287+ Style "border-bottom-width"
288+
289+
290+borderLeftWidth : String -> Style
291+borderLeftWidth =
292+ Style "border-left-width"
293+
294+
295+borderRadius : String -> Style
296+borderRadius =
297+ Style "border-radius"
298+
299+
300+borderTopLeftRadius : String -> Style
301+borderTopLeftRadius =
302+ Style "border-top-left-radius"
303+
304+
305+borderTopRightRadius : String -> Style
306+borderTopRightRadius =
307+ Style "border-top-right-radius"
308+
309+
310+borderBottomLeftRadius : String -> Style
311+borderBottomLeftRadius =
312+ Style "border-bottom-left-radius"
313+
314+
315+borderBottomRightRadius : String -> Style
316+borderBottomRightRadius =
317+ Style "border-bottom-right-radius"
318+
319+
320+borderColor : String -> Style
321+borderColor =
322+ Style "border-color"
323+
324+
325+borderStyle : String -> Style
326+borderStyle =
327+ Style "border-style"
328+
329+
330+
331+-- POSITION OFFSETS
332+
333+
334+top : String -> Style
335+top =
336+ Style "top"
337+
338+
339+right : String -> Style
340+right =
341+ Style "right"
342+
343+
344+bottom : String -> Style
345+bottom =
346+ Style "bottom"
347+
348+
349+left : String -> Style
350+left =
351+ Style "left"
352+
353+
354+
355+-- FLEXBOX
356+
357+
358+flex : String -> Style
359+flex =
360+ Style "flex"
361+
362+
363+flexGrow : String -> Style
364+flexGrow =
365+ Style "flex-grow"
366+
367+
368+flexShrink : String -> Style
369+flexShrink =
370+ Style "flex-shrink"
371+
372+
373+flexBasis : String -> Style
374+flexBasis =
375+ Style "flex-basis"
376+
377+
378+flexDirection : String -> Style
379+flexDirection =
380+ Style "flex-direction"
381+
382+
383+flexWrap : String -> Style
384+flexWrap =
385+ Style "flex-wrap"
386+
387+
388+justifyContent : String -> Style
389+justifyContent =
390+ Style "justify-content"
391+
392+
393+alignItems : String -> Style
394+alignItems =
395+ Style "align-items"
396+
397+
398+alignSelf : String -> Style
399+alignSelf =
400+ Style "align-self"
401+
402+
403+alignContent : String -> Style
404+alignContent =
405+ Style "align-content"
406+
407+
408+
409+-- LAYOUT
410+
411+
412+position : String -> Style
413+position =
414+ Style "position"
415+
416+
417+display : String -> Style
418+display =
419+ Style "display"
420+
421+
422+overflow : String -> Style
423+overflow =
424+ Style "overflow"
425+
426+
427+
428+-- VISUAL
429+
430+
431+opacity : String -> Style
432+opacity =
433+ Style "opacity"
434+
435+
436+zIndex : String -> Style
437+zIndex =
438+ Style "z-index"
439+
440+
441+backgroundColor : String -> Style
442+backgroundColor =
443+ Style "background-color"
444+
445+
446+color : String -> Style
447+color =
448+ Style "color"
449+
450+
451+
452+-- TYPOGRAPHY
453+
454+
455+fontSize : String -> Style
456+fontSize =
457+ Style "font-size"
458+
459+
460+fontWeight : String -> Style
461+fontWeight =
462+ Style "font-weight"
463+
464+
465+fontFamily : String -> Style
466+fontFamily =
467+ Style "font-family"
468+
469+
470+fontStyle : String -> Style
471+fontStyle =
472+ Style "font-style"
473+
474+
475+lineHeight : String -> Style
476+lineHeight =
477+ Style "line-height"
478+
479+
480+letterSpacing : String -> Style
481+letterSpacing =
482+ Style "letter-spacing"
483+
484+
485+textAlign : String -> Style
486+textAlign =
487+ Style "text-align"
488+
489+
490+textDecoration : String -> Style
491+textDecoration =
492+ Style "text-decoration"
493+
494+
495+textOverflow : String -> Style
496+textOverflow =
497+ Style "text-overflow"
498+
499+
500+whiteSpace : String -> Style
501+whiteSpace =
502+ Style "white-space"
503+
504+
505+wordBreak : String -> Style
506+wordBreak =
507+ Style "word-break"
508+
509+
510+
511+-- VALUE CONSTANTS
512+
513+
514+row : String
515+row =
516+ "row"
517+
518+
519+column : String
520+column =
521+ "column"
522+
523+
524+rowReverse : String
525+rowReverse =
526+ "row-reverse"
527+
528+
529+columnReverse : String
530+columnReverse =
531+ "column-reverse"
532+
533+
534+wrap : String
535+wrap =
536+ "wrap"
537+
538+
539+nowrap : String
540+nowrap =
541+ "nowrap"
542+
543+
544+flexStart : String
545+flexStart =
546+ "flex-start"
547+
548+
549+flexEnd : String
550+flexEnd =
551+ "flex-end"
552+
553+
554+center : String
555+center =
556+ "center"
557+
558+
559+spaceBetween : String
560+spaceBetween =
561+ "space-between"
562+
563+
564+spaceAround : String
565+spaceAround =
566+ "space-around"
567+
568+
569+spaceEvenly : String
570+spaceEvenly =
571+ "space-evenly"
572+
573+
574+stretch : String
575+stretch =
576+ "stretch"
577+
578+
579+baseline : String
580+baseline =
581+ "baseline"
582+
583+
584+relative : String
585+relative =
586+ "relative"
587+
588+
589+absolute : String
590+absolute =
591+ "absolute"
592+
593+
594+fixed : String
595+fixed =
596+ "fixed"
597+
598+
599+visible : String
600+visible =
601+ "visible"
602+
603+
604+hidden : String
605+hidden =
606+ "hidden"
607+
608+
609+scroll : String
610+scroll =
611+ "scroll"
612+
613+
614+bold : String
615+bold =
616+ "bold"
617+
618+
619+normal : String
620+normal =
621+ "normal"
622+
623+
624+italic : String
625+italic =
626+ "italic"
627+
628+
629+underline : String
630+underline =
631+ "underline"
632+
633+
634+lineThrough : String
635+lineThrough =
636+ "line-through"
637+
638+
639+ellipsis : String
640+ellipsis =
641+ "ellipsis"
642+
643+
644+clip : String
645+clip =
646+ "clip"
647+
648+
649+solid : String
650+solid =
651+ "solid"
652+
653+
654+dashed : String
655+dashed =
656+ "dashed"
657+
658+
659+dotted : String
660+dotted =
661+ "dotted"
662+
663+
664+{-| Escape hatch for arbitrary CSS properties.
665+-}
666+property : String -> String -> Style
667+property =
668+ Style