@gtkx/css 0.9.4 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,47 +1,43 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/HEAD/logo.svg" alt="GTKX Logo" width="128" height="128">
2
+ <img src="https://raw.githubusercontent.com/eugeniodepalo/gtkx/main/logo.svg" alt="GTKX" width="80" height="80">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">GTKX</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Build native GTK4 desktop apps with React</strong>
8
+ <strong>Build native GTK4 desktop applications with React and TypeScript.</strong>
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="https://eugeniodepalo.github.io/gtkx">Documentation</a> ·
13
- <a href="#quick-start">Quick Start</a> ·
14
- <a href="#examples">Examples</a> ·
15
- <a href="#contributing">Contributing</a>
12
+ <a href="https://www.npmjs.com/package/@gtkx/react"><img src="https://img.shields.io/npm/v/@gtkx/react.svg" alt="npm version"></a>
13
+ <a href="https://github.com/eugeniodepalo/gtkx/actions"><img src="https://img.shields.io/github/actions/workflow/status/eugeniodepalo/gtkx/ci.yml" alt="CI"></a>
14
+ <a href="https://github.com/eugeniodepalo/gtkx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MPL--2.0-blue.svg" alt="License"></a>
15
+ <a href="https://github.com/eugeniodepalo/gtkx/discussions"><img src="https://img.shields.io/badge/discussions-GitHub-blue" alt="GitHub Discussions"></a>
16
16
  </p>
17
17
 
18
18
  ---
19
19
 
20
- GTKX lets you build native Linux desktop applications using React and TypeScript. Write familiar React code that renders as native GTK4 widgets—no Electron, no web views.
21
-
22
- ## Features
23
-
24
- - **React** — Hooks, state, props, and components you already know
25
- - **HMR** — Edit code and see changes instantly via Vite
26
- - **Native** — Direct FFI bindings to GTK4 via Rust and libffi
27
- - **CLI** — `npx @gtkx/cli@latest create` scaffolds a ready-to-go project
28
- - **CSS-in-JS** — Emotion-style `css` template literals for GTK styling
29
- - **Testing** — Testing Library-style `screen`, `userEvent`, and queries
20
+ GTKX lets you write Linux desktop applications using React. Your components render as native GTK4 widgets through a Rust FFI bridge—no webviews, no Electron, just native performance with the developer experience you already know.
30
21
 
31
22
  ## Quick Start
32
23
 
33
24
  ```bash
34
- npx @gtkx/cli@latest create my-app
25
+ npx @gtkx/cli create my-app
35
26
  cd my-app
36
27
  npm run dev
37
28
  ```
38
29
 
39
- Edit your code and see changes instantly—no restart needed.
40
-
41
- ### Example
30
+ ## Example
42
31
 
43
32
  ```tsx
44
- import { render, ApplicationWindow, Box, Button, quit } from "@gtkx/react";
33
+ import {
34
+ GtkApplicationWindow,
35
+ GtkBox,
36
+ GtkButton,
37
+ GtkLabel,
38
+ quit,
39
+ render,
40
+ } from "@gtkx/react";
45
41
  import * as Gtk from "@gtkx/ffi/gtk";
46
42
  import { useState } from "react";
47
43
 
@@ -49,67 +45,59 @@ const App = () => {
49
45
  const [count, setCount] = useState(0);
50
46
 
51
47
  return (
52
- <ApplicationWindow title="Counter" onCloseRequest={quit}>
53
- <Box orientation={Gtk.Orientation.VERTICAL} spacing={12}>
54
- {`Count: ${count}`}
55
- <Button label="Increment" onClicked={() => setCount((c) => c + 1)} />
56
- </Box>
57
- </ApplicationWindow>
48
+ <GtkApplicationWindow
49
+ title="Counter"
50
+ defaultWidth={300}
51
+ defaultHeight={200}
52
+ onCloseRequest={quit}
53
+ >
54
+ <GtkBox
55
+ orientation={Gtk.Orientation.VERTICAL}
56
+ spacing={20}
57
+ valign={Gtk.Align.CENTER}
58
+ >
59
+ <GtkLabel label={`Count: ${count}`} cssClasses={["title-1"]} />
60
+ <GtkButton label="Increment" onClicked={() => setCount((c) => c + 1)} />
61
+ </GtkBox>
62
+ </GtkApplicationWindow>
58
63
  );
59
64
  };
60
65
 
61
- render(<App />, "org.example.Counter");
66
+ render(<App />, "com.example.counter");
62
67
  ```
63
68
 
64
- ## Styling
65
-
66
- ```tsx
67
- import { css } from "@gtkx/css";
68
- import { Button } from "@gtkx/react";
69
-
70
- const primary = css`
71
- padding: 16px 32px;
72
- border-radius: 24px;
73
- background: linear-gradient(135deg, #3584e4, #9141ac);
74
- color: white;
75
- `;
76
-
77
- <Button label="Click me" cssClasses={[primary]} />;
78
- ```
79
-
80
- ## Testing
81
-
82
- ```tsx
83
- import { cleanup, render, screen, userEvent } from "@gtkx/testing";
84
- import * as Gtk from "@gtkx/ffi/gtk";
85
-
86
- afterEach(() => cleanup());
69
+ ## Features
87
70
 
88
- test("increments count", async () => {
89
- await render(<App />);
71
+ - **React 19** — Hooks, concurrent features, and the component model you know
72
+ - **Native GTK4 widgets** — Real native controls, not web components in a webview
73
+ - **Adwaita support** — Modern GNOME styling with Libadwaita components
74
+ - **Hot Module Replacement** — Fast refresh during development
75
+ - **TypeScript first** — Full type safety with auto-generated bindings
76
+ - **CSS-in-JS styling** — Familiar styling patterns adapted for GTK
77
+ - **Testing utilities** — Component testing similar to Testing Library
90
78
 
91
- const button = await screen.findByRole(Gtk.AccessibleRole.BUTTON, {
92
- name: "Increment",
93
- });
79
+ ## Examples
94
80
 
95
- await userEvent.click(button);
81
+ Explore complete applications in the [`examples/`](./examples) directory:
96
82
 
97
- await screen.findByText("Count: 1");
98
- });
99
- ```
83
+ - **[gtk-demo](./examples/gtk-demo)** — Full replica of the official GTK demo app
84
+ - **[hello-world](./examples/hello-world)** — Minimal application showing a counter
85
+ - **[todo](./examples/todo)** — Full-featured todo application with Adwaita styling and testing
86
+ - **[deploying](./examples/deploying)** — Example of packaging and distributing a GTKX app
100
87
 
101
- ## Requirements
88
+ ## Documentation
102
89
 
103
- - Node.js 20+ (Deno support experimental)
104
- - GTK4 Runtime (`gtk4` on Fedora, `libgtk-4-1` on Ubuntu)
90
+ Visit [https://eugeniodepalo.github.io/gtkx](https://eugeniodepalo.github.io/gtkx/) for the full documentation.
105
91
 
106
92
  ## Contributing
107
93
 
108
- We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
94
+ Contributions are welcome! Please see the [contributing guidelines](./CONTRIBUTING.md) and check out the [good first issues](https://github.com/eugeniodepalo/gtkx/labels/good%20first%20issue).
95
+
96
+ ## Community
109
97
 
110
- - [Report a bug](https://github.com/eugeniodepalo/gtkx/issues/new?template=bug_report.md)
111
- - [Request a feature](https://github.com/eugeniodepalo/gtkx/issues/new?template=feature_request.md)
98
+ - [GitHub Discussions](https://github.com/eugeniodepalo/gtkx/discussions) — Questions, ideas, and general discussion
99
+ - [Issue Tracker](https://github.com/eugeniodepalo/gtkx/issues) — Bug reports and feature requests
112
100
 
113
101
  ## License
114
102
 
115
- [MPL-2.0](LICENSE)
103
+ [MPL-2.0](./LICENSE)
package/dist/css.d.ts CHANGED
@@ -1,48 +1,93 @@
1
1
  import type { CSSInterpolation } from "@emotion/serialize";
2
+ /**
3
+ * Branded type for CSS class names generated by {@link css}.
4
+ */
2
5
  type CSSClassName = string & {
3
6
  __brand: "css";
4
7
  };
5
8
  /**
6
- * Creates a CSS class from styles and injects them into GTK.
7
- * Works like Emotion's css function but outputs to GTK's CssProvider.
9
+ * Creates a CSS class from style definitions.
10
+ *
11
+ * Uses Emotion's CSS-in-JS system adapted for GTK CSS. Supports nested selectors
12
+ * using `&` for the parent selector reference.
13
+ *
14
+ * @param args - CSS style definitions (objects, template literals, or interpolations)
15
+ * @returns A unique class name to use with `cssClasses` prop
8
16
  *
9
17
  * @example
10
18
  * ```tsx
11
- * const buttonStyle = css`
12
- * background: @theme_bg_color;
13
- * padding: 12px;
14
- * border-radius: 6px;
15
- * `;
19
+ * import { css } from "@gtkx/css";
20
+ *
21
+ * const buttonStyle = css({
22
+ * padding: "8px 16px",
23
+ * borderRadius: "4px",
24
+ * "&:hover": {
25
+ * backgroundColor: "@accent_bg_color",
26
+ * },
27
+ * });
16
28
  *
17
- * <Button cssClasses={[buttonStyle]}>Styled Button</Button>
29
+ * <GtkButton cssClasses={[buttonStyle]} label="Styled Button" />
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * // Template literal syntax
35
+ * const labelStyle = css`
36
+ * font-size: 14px;
37
+ * color: @theme_text_color;
38
+ * `;
18
39
  * ```
40
+ *
41
+ * @see {@link cx} for combining class names
42
+ * @see {@link injectGlobal} for global styles
19
43
  */
20
44
  export declare const css: (...args: CSSInterpolation[]) => CSSClassName;
21
45
  /**
22
- * Merges multiple class names, filtering out falsy values.
46
+ * Combines multiple class names into a single space-separated string.
47
+ *
48
+ * Filters out falsy values, allowing conditional class application.
49
+ *
50
+ * @param classNames - Class names, booleans, undefined, or null values
51
+ * @returns Space-separated string of valid class names
23
52
  *
24
53
  * @example
25
54
  * ```tsx
26
- * const combined = cx(baseStyle, isActive && activeStyle, 'manual-class');
27
- * <Button cssClasses={[combined]}>Button</Button>
55
+ * import { css, cx } from "@gtkx/css";
56
+ *
57
+ * const base = css({ padding: "8px" });
58
+ * const active = css({ backgroundColor: "@accent_bg_color" });
59
+ *
60
+ * <GtkButton
61
+ * cssClasses={[cx(base, isActive && active, "custom-class")]}
62
+ * label="Button"
63
+ * />
28
64
  * ```
29
65
  */
30
66
  export declare const cx: (...classNames: (string | boolean | undefined | null)[]) => string;
31
67
  /**
32
- * Injects global CSS styles.
33
- * Note: In GTK, these styles apply to all widgets on the display.
68
+ * Injects global CSS styles without a wrapping class.
69
+ *
70
+ * Use for application-wide styles, CSS variables, or styling native GTK widgets.
71
+ *
72
+ * @param args - CSS style definitions
34
73
  *
35
74
  * @example
36
75
  * ```tsx
76
+ * import { injectGlobal } from "@gtkx/css";
77
+ *
37
78
  * injectGlobal`
38
79
  * window {
39
- * background: @theme_bg_color;
80
+ * background-color: @theme_bg_color;
40
81
  * }
41
- * button {
42
- * border-radius: 6px;
82
+ *
83
+ * .title-1 {
84
+ * font-size: 24px;
85
+ * font-weight: bold;
43
86
  * }
44
87
  * `;
45
88
  * ```
89
+ *
90
+ * @see {@link css} for scoped class-based styles
46
91
  */
47
92
  export declare const injectGlobal: (...args: CSSInterpolation[]) => void;
48
93
  export {};
package/dist/css.js CHANGED
@@ -51,19 +51,40 @@ function expandNestedRules(styles, className) {
51
51
  return allRules.join("\n");
52
52
  }
53
53
  /**
54
- * Creates a CSS class from styles and injects them into GTK.
55
- * Works like Emotion's css function but outputs to GTK's CssProvider.
54
+ * Creates a CSS class from style definitions.
55
+ *
56
+ * Uses Emotion's CSS-in-JS system adapted for GTK CSS. Supports nested selectors
57
+ * using `&` for the parent selector reference.
58
+ *
59
+ * @param args - CSS style definitions (objects, template literals, or interpolations)
60
+ * @returns A unique class name to use with `cssClasses` prop
56
61
  *
57
62
  * @example
58
63
  * ```tsx
59
- * const buttonStyle = css`
60
- * background: @theme_bg_color;
61
- * padding: 12px;
62
- * border-radius: 6px;
63
- * `;
64
+ * import { css } from "@gtkx/css";
65
+ *
66
+ * const buttonStyle = css({
67
+ * padding: "8px 16px",
68
+ * borderRadius: "4px",
69
+ * "&:hover": {
70
+ * backgroundColor: "@accent_bg_color",
71
+ * },
72
+ * });
73
+ *
74
+ * <GtkButton cssClasses={[buttonStyle]} label="Styled Button" />
75
+ * ```
64
76
  *
65
- * <Button cssClasses={[buttonStyle]}>Styled Button</Button>
77
+ * @example
78
+ * ```tsx
79
+ * // Template literal syntax
80
+ * const labelStyle = css`
81
+ * font-size: 14px;
82
+ * color: @theme_text_color;
83
+ * `;
66
84
  * ```
85
+ *
86
+ * @see {@link cx} for combining class names
87
+ * @see {@link injectGlobal} for global styles
67
88
  */
68
89
  export const css = (...args) => {
69
90
  const cache = getGtkCache();
@@ -78,30 +99,51 @@ export const css = (...args) => {
78
99
  return className;
79
100
  };
80
101
  /**
81
- * Merges multiple class names, filtering out falsy values.
102
+ * Combines multiple class names into a single space-separated string.
103
+ *
104
+ * Filters out falsy values, allowing conditional class application.
105
+ *
106
+ * @param classNames - Class names, booleans, undefined, or null values
107
+ * @returns Space-separated string of valid class names
82
108
  *
83
109
  * @example
84
110
  * ```tsx
85
- * const combined = cx(baseStyle, isActive && activeStyle, 'manual-class');
86
- * <Button cssClasses={[combined]}>Button</Button>
111
+ * import { css, cx } from "@gtkx/css";
112
+ *
113
+ * const base = css({ padding: "8px" });
114
+ * const active = css({ backgroundColor: "@accent_bg_color" });
115
+ *
116
+ * <GtkButton
117
+ * cssClasses={[cx(base, isActive && active, "custom-class")]}
118
+ * label="Button"
119
+ * />
87
120
  * ```
88
121
  */
89
122
  export const cx = (...classNames) => classNames.filter((cn) => typeof cn === "string" && cn.length > 0).join(" ");
90
123
  /**
91
- * Injects global CSS styles.
92
- * Note: In GTK, these styles apply to all widgets on the display.
124
+ * Injects global CSS styles without a wrapping class.
125
+ *
126
+ * Use for application-wide styles, CSS variables, or styling native GTK widgets.
127
+ *
128
+ * @param args - CSS style definitions
93
129
  *
94
130
  * @example
95
131
  * ```tsx
132
+ * import { injectGlobal } from "@gtkx/css";
133
+ *
96
134
  * injectGlobal`
97
135
  * window {
98
- * background: @theme_bg_color;
136
+ * background-color: @theme_bg_color;
99
137
  * }
100
- * button {
101
- * border-radius: 6px;
138
+ *
139
+ * .title-1 {
140
+ * font-size: 24px;
141
+ * font-weight: bold;
102
142
  * }
103
143
  * `;
104
144
  * ```
145
+ *
146
+ * @see {@link css} for scoped class-based styles
105
147
  */
106
148
  export const injectGlobal = (...args) => {
107
149
  const cache = getGtkCache();
@@ -13,12 +13,9 @@ const flushPendingStyles = () => {
13
13
  };
14
14
  const resetGtkState = () => {
15
15
  isGtkReady = false;
16
- // Re-register the start listener for the next start() call
17
16
  events.once("start", flushPendingStyles);
18
17
  };
19
- // Initial registration
20
18
  events.once("start", flushPendingStyles);
21
- // Handle stop to reset state and prepare for potential restart
22
19
  events.on("stop", resetGtkState);
23
20
  export class StyleSheet {
24
21
  key;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/css",
3
- "version": "0.9.4",
3
+ "version": "0.10.0",
4
4
  "description": "Emotion-style CSS-in-JS for GTKX applications",
5
5
  "keywords": [
6
6
  "gtk",
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@emotion/cache": "^11.14.0",
37
37
  "@emotion/serialize": "^1.3.3",
38
- "@gtkx/ffi": "0.9.4"
38
+ "@gtkx/ffi": "0.10.0"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -b && cp ../../README.md .",