@k9kbdev/roblox-css 0.1.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.
Files changed (55) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +245 -0
  3. package/default.project.json +6 -0
  4. package/out/index.d.ts +35 -0
  5. package/out/init.luau +57 -0
  6. package/out/logger.d.ts +23 -0
  7. package/out/logger.luau +73 -0
  8. package/out/primitives/Box.d.ts +23 -0
  9. package/out/primitives/Box.luau +103 -0
  10. package/out/primitives/Button.d.ts +62 -0
  11. package/out/primitives/Button.luau +170 -0
  12. package/out/primitives/Image.d.ts +37 -0
  13. package/out/primitives/Image.luau +79 -0
  14. package/out/primitives/InlineText.d.ts +25 -0
  15. package/out/primitives/InlineText.luau +273 -0
  16. package/out/primitives/Input.d.ts +59 -0
  17. package/out/primitives/Input.luau +126 -0
  18. package/out/primitives/MotionBox.d.ts +15 -0
  19. package/out/primitives/MotionBox.luau +69 -0
  20. package/out/primitives/MotionButton.d.ts +15 -0
  21. package/out/primitives/MotionButton.luau +146 -0
  22. package/out/primitives/MotionImage.d.ts +13 -0
  23. package/out/primitives/MotionImage.luau +70 -0
  24. package/out/primitives/MotionText.d.ts +12 -0
  25. package/out/primitives/MotionText.luau +116 -0
  26. package/out/primitives/MotionUIScale.d.ts +9 -0
  27. package/out/primitives/MotionUIScale.luau +48 -0
  28. package/out/primitives/ScrollBox.d.ts +25 -0
  29. package/out/primitives/ScrollBox.luau +69 -0
  30. package/out/primitives/Text.d.ts +50 -0
  31. package/out/primitives/Text.luau +139 -0
  32. package/out/primitives/usePercentageConstraints.d.ts +3 -0
  33. package/out/primitives/usePercentageConstraints.luau +112 -0
  34. package/out/primitives/useVariantResolver.d.ts +13 -0
  35. package/out/primitives/useVariantResolver.luau +260 -0
  36. package/out/styles/CSSTypes.d.ts +96 -0
  37. package/out/styles/ParentSizeContext.d.ts +6 -0
  38. package/out/styles/ParentSizeContext.luau +13 -0
  39. package/out/styles/colorParser.d.ts +28 -0
  40. package/out/styles/colorParser.luau +229 -0
  41. package/out/styles/dimensionParser.d.ts +49 -0
  42. package/out/styles/dimensionParser.luau +205 -0
  43. package/out/styles/gradientParser.d.ts +9 -0
  44. package/out/styles/gradientParser.luau +434 -0
  45. package/out/styles/namedColors.d.ts +7 -0
  46. package/out/styles/namedColors.luau +162 -0
  47. package/out/styles/transitions.d.ts +18 -0
  48. package/out/styles/transitions.luau +19 -0
  49. package/out/styles/webStyle.d.ts +74 -0
  50. package/out/styles/webStyle.luau +973 -0
  51. package/out/types.d.ts +4 -0
  52. package/out/types.luau +3 -0
  53. package/out/utils/parseInlineImages.d.ts +20 -0
  54. package/out/utils/parseInlineImages.luau +93 -0
  55. package/package.json +56 -0
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ roblox-css — CSS-to-Roblox UI translation middleware
2
+ Copyright (C) 2026 Kaleb Kougl
3
+
4
+ This library is free software; you can redistribute it and/or modify it
5
+ under the terms of the GNU Lesser General Public License as published by
6
+ the Free Software Foundation; either version 3 of the License, or (at
7
+ your option) any later version.
8
+
9
+ This library is distributed in the hope that it will be useful, but
10
+ WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
12
+ General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public License
15
+ along with this library. If not, see <https://www.gnu.org/licenses/>.
package/README.md ADDED
@@ -0,0 +1,245 @@
1
+ # roblox-css
2
+
3
+ **Write CSS. Get Roblox UI.**
4
+
5
+ [![License: LGPL-3.0](https://img.shields.io/badge/License-LGPL--3.0-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
6
+
7
+ A CSS-to-Roblox UI translation middleware for [roblox-ts](https://roblox-ts.com). Write familiar web-style `style` props and get native Roblox engine primitives (`UDim2`, `UICorner`, `UIListLayout`, etc.) automatically.
8
+
9
+ ## What Problem Does This Solve?
10
+
11
+ Building UI in Roblox requires manually wiring up `UDim2` sizes, parenting constraint instances (`UICorner`, `UIPadding`, `UIListLayout`), and juggling imperative property sets. `roblox-css` lets you write this instead:
12
+
13
+ ```tsx
14
+ import { Box, Text, webStyle } from "roblox-css";
15
+
16
+ const card = webStyle({
17
+ width: "200px",
18
+ height: "auto",
19
+ padding: "16px",
20
+ borderRadius: "12px",
21
+ backgroundColor: "#1a1a2e",
22
+ display: "flex",
23
+ flexDirection: "column",
24
+ gap: "8px",
25
+ });
26
+
27
+ // In your component:
28
+ <Box style={card}>
29
+ <Text style={webStyle({ color: "white", fontSize: "18px" })}>
30
+ Hello, Roblox!
31
+ </Text>
32
+ </Box>
33
+ ```
34
+
35
+ The `webStyle()` function translates CSS properties into Roblox instance properties and child constraint elements. The wrapper components (`Box`, `Text`, `Button`, etc.) spread the result onto native Roblox instances.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ npm install roblox-css
41
+ ```
42
+
43
+ ### Peer Dependencies
44
+
45
+ **Required:**
46
+ - `@rbxts/react` (>=17.0.0)
47
+
48
+ **Optional** (only needed for motion primitives):
49
+ - `@rbxts/ripple` (>=0.8.0)
50
+ - `@rbxts/services`
51
+
52
+ ### Rojo Configuration
53
+
54
+ Add `roblox-css` to your Rojo project file:
55
+
56
+ ```json
57
+ {
58
+ "ReplicatedStorage": {
59
+ "roblox-css": {
60
+ "$path": "node_modules/roblox-css/src"
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ ## Primitives
67
+
68
+ | Component | HTML Equivalent | Roblox Instance |
69
+ |:---|:---|:---|
70
+ | `<Box>` | `<div>` | `<frame>` |
71
+ | `<Text>` | `<span>` / `<p>` | `<textlabel>` |
72
+ | `<Button>` | `<button>` | `<textbutton>` |
73
+ | `<Image>` | `<img>` | `<imagelabel>` |
74
+ | `<Input>` | `<input>` | `<textbox>` |
75
+ | `<ScrollBox>` | `<div style="overflow:auto">` | `<scrollingframe>` |
76
+
77
+ ### Motion Primitives
78
+
79
+ Declarative, variant-driven animation (inspired by Framer Motion):
80
+
81
+ | Component | Animates |
82
+ |:---|:---|
83
+ | `<MotionBox>` | `<frame>` |
84
+ | `<MotionText>` | `<textlabel>` |
85
+ | `<MotionButton>` | `<textbutton>` |
86
+ | `<MotionImage>` | `<imagelabel>` |
87
+ | `<MotionUIScale>` | `<uiscale>` |
88
+
89
+ ```tsx
90
+ import { MotionBox, webStyle } from "roblox-css";
91
+
92
+ <MotionBox
93
+ style={webStyle({ width: "100px", height: "100px" })}
94
+ animate={isHovered ? "hover" : "idle"}
95
+ variants={{
96
+ idle: { backgroundColor: "#1a1a2e", width: "100px" },
97
+ hover: { backgroundColor: "#e94560", width: "120px" },
98
+ }}
99
+ transition={new TweenInfo(0.3, Enum.EasingStyle.Quad)}
100
+ />
101
+ ```
102
+
103
+ Requires `@rbxts/ripple` peer dependency.
104
+
105
+ ## Supported CSS Properties
106
+
107
+ ### Layout
108
+ | CSS Property | Roblox Mapping |
109
+ |:---|:---|
110
+ | `width`, `height` | `Size` (UDim2) |
111
+ | `minWidth`, `maxWidth`, `minHeight`, `maxHeight` | `UISizeConstraint` |
112
+ | `display: "flex"` | `UIListLayout` |
113
+ | `display: "grid"` | `UIGridLayout` |
114
+ | `flexDirection` | `UIListLayout.FillDirection` |
115
+ | `justifyContent` | `UIListLayout.HorizontalAlignment` / `VerticalAlignment` |
116
+ | `alignItems` | `UIListLayout.HorizontalAlignment` / `VerticalAlignment` |
117
+ | `gap` | `UIListLayout.Padding` |
118
+ | `flexWrap` | `UIListLayout.Wraps` |
119
+ | `order` | `LayoutOrder` |
120
+
121
+ ### Spacing
122
+ | CSS Property | Roblox Mapping |
123
+ |:---|:---|
124
+ | `padding` (shorthand) | `UIPadding` (all sides) |
125
+ | `paddingTop/Right/Bottom/Left` | `UIPadding` (individual) |
126
+
127
+ ### Visual
128
+ | CSS Property | Roblox Mapping |
129
+ |:---|:---|
130
+ | `backgroundColor` | `BackgroundColor3` + `BackgroundTransparency` |
131
+ | `color` | `TextColor3` + `TextTransparency` |
132
+ | `opacity` | `BackgroundTransparency` / `ImageTransparency` |
133
+ | `borderRadius` | `UICorner` |
134
+ | `border` (shorthand) | `BorderSizePixel` + `BorderColor3` (style keywords like `solid` are optional) |
135
+ | `boxShadow` | 9-slice `ImageLabel` overlay |
136
+ | `background: "linear-gradient(...)"` | `UIGradient` |
137
+
138
+ ### Typography
139
+ | CSS Property | Roblox Mapping |
140
+ |:---|:---|
141
+ | `fontSize` | `TextSize` |
142
+ | `fontWeight` | `FontFace.Weight` |
143
+ | `fontFamily` | `FontFace.Family` |
144
+ | `fontStyle` | `FontFace.Style` |
145
+ | `textAlign` | `TextXAlignment` |
146
+ | `textVerticalAlign` | `TextYAlignment` |
147
+ | `lineHeight` | `LineHeight` |
148
+ | `textDecoration` | Rich text `<u>` / `<s>` tags |
149
+ | `textTransform` | Component-level string transform |
150
+ | `wordBreak` | Zero-width space injection |
151
+ | `whiteSpace` | `TextWrapped` |
152
+ | `textOverflow` | `TextTruncate` |
153
+
154
+ ### Other
155
+ | CSS Property | Roblox Mapping |
156
+ |:---|:---|
157
+ | `overflow: "scroll"` | `ScrollingEnabled` |
158
+ | `objectFit` | `ScaleType` |
159
+ | `cursor` | Not supported in Roblox |
160
+ | `visibility` | `Visible` |
161
+ | `rotation` | `Rotation` |
162
+ | `zIndex` | `ZIndex` |
163
+ | `aspectRatio` | `UIAspectRatioConstraint` |
164
+ | `autoSize` / `width: "auto"` | `AutomaticSize` |
165
+
166
+ ### Color Formats
167
+
168
+ `roblox-css` supports all standard CSS color formats:
169
+
170
+ - **Hex:** `#ff0000`, `#f00`, `#ff000080`
171
+ - **RGB:** `rgb(255, 0, 0)`, `rgba(255, 0, 0, 0.5)`
172
+ - **HSL:** `hsl(0, 100%, 50%)`, `hsla(0, 100%, 50%, 0.5)`
173
+ - **Named:** All 148 CSS named colors (`red`, `cornflowerblue`, `rebeccapurple`, etc.)
174
+ - **Roblox Color3:** Pass `Color3` instances directly
175
+
176
+ ### Dimension Formats
177
+
178
+ - **Pixels:** `"100px"` or `100` (bare number = pixels)
179
+ - **Percentage:** `"50%"` → `UDim` scale component
180
+ - **Viewport:** `"100vw"`, `"50vh"` → viewport-relative
181
+ - **Calc:** `"calc(100% - 20px)"`
182
+ - **Auto:** `"auto"` → `AutomaticSize`
183
+
184
+ ## Type Safety
185
+
186
+ `roblox-css` uses TypeScript **branded types** to prevent bypassing the middleware at compile time:
187
+
188
+ ```typescript
189
+ // ✅ This works — value produced by webStyle()
190
+ const style = webStyle({ width: "100px" });
191
+ <Box style={style} />
192
+
193
+ // ❌ This fails at compile time — raw object rejected
194
+ const fake = { props: { Size: UDim2.fromOffset(100, 0) }, children: [] };
195
+ <Box style={fake} /> // Type error: missing branded symbol
196
+ ```
197
+
198
+ Four branded types enforce this: `WebStyleResult`, `ParsedDimension`, `ParsedColor`, `PaddingValues`.
199
+
200
+ ## Architecture
201
+
202
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full architectural deep-dive.
203
+
204
+ ```
205
+ CSSProperties → webStyle() → { props, children }
206
+
207
+ ┌──────────┼──────────┐
208
+ ▼ ▼ ▼
209
+ colorParser dimParser gradientParser
210
+ │ │ │
211
+ ▼ ▼ ▼
212
+ Color3 UDim/UDim2 UIGradient
213
+ │ │ │
214
+ └──────────┼──────────┘
215
+
216
+ WebStyleResult (branded)
217
+
218
+
219
+ <Box style={result} />
220
+
221
+ ┌───────┼───────┐
222
+ ▼ ▼
223
+ props spread constraint children
224
+ on <frame> (UICorner, UIPadding, ...)
225
+ ```
226
+
227
+ ## Running Tests
228
+
229
+ Tests run natively inside Roblox Studio using `@rbxts/jest`:
230
+
231
+ 1. Build: `npx rbxtsc`
232
+ 2. Open `test-runner.project.json` in Roblox Studio via Rojo
233
+ 3. Run the Jest test plugin
234
+
235
+ **Test coverage:** 1,419 assertions across 24 spec files covering every parser, primitive, and motion component.
236
+
237
+ ## Contributing
238
+
239
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
240
+
241
+ ## License
242
+
243
+ LGPL-3.0-only — see [LICENSE](LICENSE), [COPYING](COPYING), and [COPYING.LESSER](COPYING.LESSER).
244
+
245
+ You may use `roblox-css` in proprietary projects without releasing your project's source code. However, any modifications to `roblox-css` itself must be released under LGPL-3.0.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "roblox-css",
3
+ "tree": {
4
+ "$path": "out"
5
+ }
6
+ }
package/out/index.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * roblox-css — CSS-to-Roblox UI translation middleware.
3
+ *
4
+ * @license LGPL-3.0-only
5
+ * @author Kaleb Kougl
6
+ */
7
+ export { webStyle, SHADOW_ASSET_ID, SHADOW_SLICE_CENTER } from "./styles/webStyle";
8
+ export type { CSSProperties } from "./styles/CSSTypes";
9
+ export { parseColor } from "./styles/colorParser";
10
+ export { parseDimension, parsePadding } from "./styles/dimensionParser";
11
+ export { parseGradient } from "./styles/gradientParser";
12
+ export { NAMED_COLORS } from "./styles/namedColors";
13
+ export { transitions } from "./styles/transitions";
14
+ export { ParentSizeContext } from "./styles/ParentSizeContext";
15
+ export { Box } from "./primitives/Box";
16
+ export type { BoxProps } from "./primitives/Box";
17
+ export { Text } from "./primitives/Text";
18
+ export { Button } from "./primitives/Button";
19
+ export { Image } from "./primitives/Image";
20
+ export { Input } from "./primitives/Input";
21
+ export { ScrollBox } from "./primitives/ScrollBox";
22
+ export { InlineText } from "./primitives/InlineText";
23
+ export { parseInlineImages, containsRichTextTags } from "./utils/parseInlineImages";
24
+ export type { InlineSegment, TextSegment, ImageSegment } from "./utils/parseInlineImages";
25
+ export { MotionBox } from "./primitives/MotionBox";
26
+ export type { MotionBoxProps } from "./primitives/MotionBox";
27
+ export { MotionText } from "./primitives/MotionText";
28
+ export { MotionButton } from "./primitives/MotionButton";
29
+ export { MotionImage } from "./primitives/MotionImage";
30
+ export { MotionUIScale } from "./primitives/MotionUIScale";
31
+ export { useVariantResolver, isAnimatable } from "./primitives/useVariantResolver";
32
+ export type { MotionProps } from "./primitives/useVariantResolver";
33
+ export { usePercentageConstraints } from "./primitives/usePercentageConstraints";
34
+ export { setLogLevel, LogLevel } from "./logger";
35
+ export type { DeepReadonly } from "./types";
package/out/init.luau ADDED
@@ -0,0 +1,57 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local exports = {}
4
+ --[[
5
+ *
6
+ * roblox-css — CSS-to-Roblox UI translation middleware.
7
+ *
8
+ * @license LGPL-3.0-only
9
+ * @author Kaleb Kougl
10
+
11
+ ]]
12
+ -- Core engine
13
+ local _webStyle = TS.import(script, script, "styles", "webStyle")
14
+ exports.webStyle = _webStyle.webStyle
15
+ exports.SHADOW_ASSET_ID = _webStyle.SHADOW_ASSET_ID
16
+ exports.SHADOW_SLICE_CENTER = _webStyle.SHADOW_SLICE_CENTER
17
+ -- Parsers (for advanced users)
18
+ exports.parseColor = TS.import(script, script, "styles", "colorParser").parseColor
19
+ local _dimensionParser = TS.import(script, script, "styles", "dimensionParser")
20
+ exports.parseDimension = _dimensionParser.parseDimension
21
+ exports.parsePadding = _dimensionParser.parsePadding
22
+ exports.parseGradient = TS.import(script, script, "styles", "gradientParser").parseGradient
23
+ -- Named colors
24
+ exports.NAMED_COLORS = TS.import(script, script, "styles", "namedColors").NAMED_COLORS
25
+ -- Transitions
26
+ exports.transitions = TS.import(script, script, "styles", "transitions").transitions
27
+ -- Context
28
+ exports.ParentSizeContext = TS.import(script, script, "styles", "ParentSizeContext").ParentSizeContext
29
+ -- Base primitives
30
+ exports.Box = TS.import(script, script, "primitives", "Box").Box
31
+ exports.Text = TS.import(script, script, "primitives", "Text").Text
32
+ exports.Button = TS.import(script, script, "primitives", "Button").Button
33
+ exports.Image = TS.import(script, script, "primitives", "Image").Image
34
+ exports.Input = TS.import(script, script, "primitives", "Input").Input
35
+ exports.ScrollBox = TS.import(script, script, "primitives", "ScrollBox").ScrollBox
36
+ exports.InlineText = TS.import(script, script, "primitives", "InlineText").InlineText
37
+ -- Inline image parser (for advanced users)
38
+ local _parseInlineImages = TS.import(script, script, "utils", "parseInlineImages")
39
+ exports.parseInlineImages = _parseInlineImages.parseInlineImages
40
+ exports.containsRichTextTags = _parseInlineImages.containsRichTextTags
41
+ -- Motion primitives
42
+ exports.MotionBox = TS.import(script, script, "primitives", "MotionBox").MotionBox
43
+ exports.MotionText = TS.import(script, script, "primitives", "MotionText").MotionText
44
+ exports.MotionButton = TS.import(script, script, "primitives", "MotionButton").MotionButton
45
+ exports.MotionImage = TS.import(script, script, "primitives", "MotionImage").MotionImage
46
+ exports.MotionUIScale = TS.import(script, script, "primitives", "MotionUIScale").MotionUIScale
47
+ -- Motion hooks
48
+ local _useVariantResolver = TS.import(script, script, "primitives", "useVariantResolver")
49
+ exports.useVariantResolver = _useVariantResolver.useVariantResolver
50
+ exports.isAnimatable = _useVariantResolver.isAnimatable
51
+ exports.usePercentageConstraints = TS.import(script, script, "primitives", "usePercentageConstraints").usePercentageConstraints
52
+ -- Logger (for consumers who want to configure logging level)
53
+ local _logger = TS.import(script, script, "logger")
54
+ exports.setLogLevel = _logger.setLogLevel
55
+ exports.LogLevel = _logger.LogLevel
56
+ -- Types
57
+ return exports
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Minimal structured logger for roblox-css.
3
+ *
4
+ * Provides a `createLogger(source)` function that returns a logger object
5
+ * with `debug`, `info`, `warn`, `error`, and `fatal` methods.
6
+ *
7
+ * Each method accepts: (code: string, message: string, data?: object)
8
+ */
9
+ export declare enum LogLevel {
10
+ DEBUG = 0,
11
+ INFO = 1,
12
+ WARN = 2,
13
+ ERROR = 3,
14
+ FATAL = 4
15
+ }
16
+ export declare function setLogLevel(level: LogLevel): void;
17
+ export declare function createLogger(source: string): {
18
+ debug(code: string, message: string, data?: object): void;
19
+ info(code: string, message: string, data?: object): void;
20
+ warn(code: string, message: string, data?: object): void;
21
+ error(code: string, message: string, data?: object): void;
22
+ fatal(code: string, message: string, data?: object): void;
23
+ };
@@ -0,0 +1,73 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ --[[
3
+ *
4
+ * Minimal structured logger for roblox-css.
5
+ *
6
+ * Provides a `createLogger(source)` function that returns a logger object
7
+ * with `debug`, `info`, `warn`, `error`, and `fatal` methods.
8
+ *
9
+ * Each method accepts: (code: string, message: string, data?: object)
10
+
11
+ ]]
12
+ local LogLevel
13
+ do
14
+ local _inverse = {}
15
+ LogLevel = setmetatable({}, {
16
+ __index = _inverse,
17
+ })
18
+ LogLevel.DEBUG = 0
19
+ _inverse[0] = "DEBUG"
20
+ LogLevel.INFO = 1
21
+ _inverse[1] = "INFO"
22
+ LogLevel.WARN = 2
23
+ _inverse[2] = "WARN"
24
+ LogLevel.ERROR = 3
25
+ _inverse[3] = "ERROR"
26
+ LogLevel.FATAL = 4
27
+ _inverse[4] = "FATAL"
28
+ end
29
+ local currentLogLevel = LogLevel.WARN
30
+ local function setLogLevel(level)
31
+ currentLogLevel = level
32
+ end
33
+ local function formatLogLine(level, source, code, message, data)
34
+ local line = `[{level}][{source}] {code}: {message}`
35
+ if data then
36
+ for k, v in pairs(data) do
37
+ line ..= ` {k}={tostring(v)}`
38
+ end
39
+ end
40
+ return line
41
+ end
42
+ local function createLogger(source)
43
+ return {
44
+ debug = function(self, code, message, data)
45
+ if currentLogLevel <= LogLevel.DEBUG then
46
+ print(formatLogLine("DEBUG", source, code, message, data))
47
+ end
48
+ end,
49
+ info = function(self, code, message, data)
50
+ if currentLogLevel <= LogLevel.INFO then
51
+ print(formatLogLine("INFO", source, code, message, data))
52
+ end
53
+ end,
54
+ warn = function(self, code, message, data)
55
+ if currentLogLevel <= LogLevel.WARN then
56
+ warn(formatLogLine("WARN", source, code, message, data))
57
+ end
58
+ end,
59
+ error = function(self, code, message, data)
60
+ if currentLogLevel <= LogLevel.ERROR then
61
+ warn(formatLogLine("ERROR", source, code, message, data))
62
+ end
63
+ end,
64
+ fatal = function(self, code, message, data)
65
+ warn(formatLogLine("FATAL", source, code, message, data))
66
+ end,
67
+ }
68
+ end
69
+ return {
70
+ setLogLevel = setLogLevel,
71
+ createLogger = createLogger,
72
+ LogLevel = LogLevel,
73
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Box.tsx — The foundational UI container component for Roblox.
3
+ *
4
+ * Provides a <div> equivalent that supports a web-like CSS syntax (`style` prop).
5
+ * It automatically translates CSS properties (e.g., `backgroundColor: "red"`) into
6
+ * Roblox engine equivalents (`BackgroundColor3`) and injects layout constraints
7
+ * like `UICorner`, `UIPadding`, `UIListLayout`, and `UIGridLayout` as child instances.
8
+ *
9
+ * Usage:
10
+ * <Box style={{ width: "100%", height: "50px", backgroundColor: "#333", borderRadius: "8px" }}>
11
+ * <textlabel Text="Hello World" />
12
+ * </Box>
13
+ */
14
+ import React from "@rbxts/react";
15
+ import { CSSProperties } from "../styles/webStyle";
16
+ import { DeepReadonly } from "../types";
17
+ export type BoxProps = React.PropsWithChildren<React.ComponentProps<"frame">> & {
18
+ style?: CSSProperties;
19
+ } & {
20
+ readonly _boxProps?: unique symbol;
21
+ };
22
+ export declare function makeBoxProps(props: Omit<BoxProps, "_boxProps">): DeepReadonly<BoxProps>;
23
+ export declare const Box: React.ForwardRefExoticComponent<Omit<BoxProps, "ref"> & React.RefAttributes<Frame>>;
@@ -0,0 +1,103 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ --[[
4
+ *
5
+ * Box.tsx — The foundational UI container component for Roblox.
6
+ *
7
+ * Provides a <div> equivalent that supports a web-like CSS syntax (`style` prop).
8
+ * It automatically translates CSS properties (e.g., `backgroundColor: "red"`) into
9
+ * Roblox engine equivalents (`BackgroundColor3`) and injects layout constraints
10
+ * like `UICorner`, `UIPadding`, `UIListLayout`, and `UIGridLayout` as child instances.
11
+ *
12
+ * Usage:
13
+ * <Box style={{ width: "100%", height: "50px", backgroundColor: "#333", borderRadius: "8px" }}>
14
+ * <textlabel Text="Hello World" />
15
+ * </Box>
16
+
17
+ ]]
18
+ local _react = TS.import(script, TS.getModule(script, "@rbxts", "react"))
19
+ local React = _react
20
+ local useRef = _react.useRef
21
+ local webStyle = TS.import(script, script.Parent.Parent, "styles", "webStyle").webStyle
22
+ local ParentSizeContext = TS.import(script, script.Parent.Parent, "styles", "ParentSizeContext").ParentSizeContext
23
+ local usePercentageConstraints = TS.import(script, script.Parent, "usePercentageConstraints").usePercentageConstraints
24
+ local function makeBoxProps(props)
25
+ return props
26
+ end
27
+ local Box = React.forwardRef(function(props, ref)
28
+ local style = props.style
29
+ local children = props.children
30
+ local defaultProps = {
31
+ BackgroundTransparency = 1,
32
+ BorderSizePixel = 0,
33
+ }
34
+ local _object = table.clone(props)
35
+ setmetatable(_object, nil)
36
+ local explicitProps = _object
37
+ explicitProps.style = nil
38
+ explicitProps.children = nil
39
+ -- Merge user-provided Change listeners with our internal AbsoluteSize tracker
40
+ -- to avoid silently overwriting user event handlers.
41
+ local userChange = (props.Change) or {}
42
+ local _object_1 = table.clone(userChange)
43
+ setmetatable(_object_1, nil)
44
+ local mergedChange = _object_1
45
+ local setAbsSize
46
+ mergedChange.AbsoluteSize = function(rbx)
47
+ setAbsSize(rbx.AbsoluteSize)
48
+ -- Forward to user handler if present
49
+ local userHandler = userChange.AbsoluteSize
50
+ if userHandler ~= nil then
51
+ userHandler(rbx)
52
+ end
53
+ end
54
+ explicitProps.Change = nil
55
+ local bindingRef = useRef()
56
+ if not bindingRef.current then
57
+ local binding, setBinding = React.createBinding(Vector2.new(math.huge, math.huge))
58
+ bindingRef.current = {
59
+ binding = binding,
60
+ set = setBinding,
61
+ }
62
+ end
63
+ local _binding = bindingRef.current
64
+ local absSize = _binding.binding
65
+ setAbsSize = _binding.set
66
+ local percentageConstraint = usePercentageConstraints(style)
67
+ if style then
68
+ local parsedStyle = webStyle(style)
69
+ local _attributes = {
70
+ ref = ref,
71
+ }
72
+ for _k, _v in defaultProps do
73
+ _attributes[_k] = _v
74
+ end
75
+ for _k, _v in parsedStyle.props do
76
+ _attributes[_k] = _v
77
+ end
78
+ for _k, _v in explicitProps do
79
+ _attributes[_k] = _v
80
+ end
81
+ _attributes.Change = mergedChange
82
+ return React.createElement(ParentSizeContext.Provider, {
83
+ value = absSize,
84
+ }, React.createElement("frame", _attributes, parsedStyle.children, percentageConstraint, children))
85
+ end
86
+ local _attributes = {
87
+ ref = ref,
88
+ }
89
+ for _k, _v in defaultProps do
90
+ _attributes[_k] = _v
91
+ end
92
+ for _k, _v in explicitProps do
93
+ _attributes[_k] = _v
94
+ end
95
+ _attributes.Change = mergedChange
96
+ return React.createElement(ParentSizeContext.Provider, {
97
+ value = absSize,
98
+ }, React.createElement("frame", _attributes, children))
99
+ end)
100
+ return {
101
+ makeBoxProps = makeBoxProps,
102
+ Box = Box,
103
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Button.tsx — <button> equivalent for Roblox.
3
+ *
4
+ * Renders a <textbutton> with web-like style props.
5
+ * Delegates CSS→Roblox translation to webStyle(), and adds text-content
6
+ * manipulation features (textTransform, textDecoration, wordBreak) that
7
+ * require mutating the Text string before rendering.
8
+ * Typography mapping:
9
+ * CSS Property → Roblox Output
10
+ * ─────────────────────────────────────────────────────────
11
+ * fontSize → TextSize (number)
12
+ * fontFamily → FontFace (Font object, defaults to BuilderSans)
13
+ * fontWeight → FontFace weight (normal/bold/black → Regular/Bold/Heavy)
14
+ * textAlign → TextXAlignment (left/center/right)
15
+ * whiteSpace → TextWrapped (nowrap → false, otherwise true)
16
+ * color → TextColor3 (Color3) + TextTransparency
17
+ *
18
+ * Event mapping:
19
+ * React Prop → Roblox Output
20
+ * ─────────────────────────────────────────────────────────
21
+ * onClick → Event.Activated
22
+ * onMouseEnter → Event.MouseEnter
23
+ * onMouseLeave → Event.MouseLeave
24
+ *
25
+ * Maps to: HTML <button> → Roblox <textbutton>
26
+ *
27
+ * Usage:
28
+ * <Button
29
+ * style={{ backgroundColor: "#007BFF", padding: "10px 20px", color: "white", borderRadius: "4px" }}
30
+ * onClick={() => print("Clicked!")}
31
+ * >
32
+ * Submit
33
+ * </Button>
34
+ */
35
+ import React from "@rbxts/react";
36
+ import { CSSProperties } from "../styles/CSSTypes";
37
+ import { DeepReadonly } from "../types";
38
+ /**
39
+ * Branded ButtonProps — the `_buttonProps` brand ensures strict nominal typing.
40
+ * This prevents accidental bypasses or incorrect prop structures.
41
+ */
42
+ export type ButtonProps = React.PropsWithChildren<React.ComponentProps<"textbutton">> & {
43
+ /**
44
+ * Web-like CSS styling object that automatically maps to Roblox properties
45
+ * and injects necessary UI constraints (UICorner, UIPadding, UIListLayout).
46
+ */
47
+ style?: CSSProperties;
48
+ onClick?: () => void;
49
+ onMouseEnter?: () => void;
50
+ onMouseLeave?: () => void;
51
+ } & {
52
+ readonly _buttonProps?: unique symbol;
53
+ };
54
+ /** Helper to construct a branded ButtonProps. */
55
+ export declare function makeButtonProps(props: Omit<ButtonProps, "_buttonProps">): DeepReadonly<ButtonProps>;
56
+ /**
57
+ * Button component — The primary component for interactive clickable elements.
58
+ *
59
+ * Maps to: HTML <button> → Roblox <textbutton>
60
+ * Renders a native <textbutton>, mapping web styles and React-like event handlers (onClick, etc.).
61
+ */
62
+ export declare const Button: React.ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & React.RefAttributes<TextButton>>;