@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.
- package/LICENSE +15 -0
- package/README.md +245 -0
- package/default.project.json +6 -0
- package/out/index.d.ts +35 -0
- package/out/init.luau +57 -0
- package/out/logger.d.ts +23 -0
- package/out/logger.luau +73 -0
- package/out/primitives/Box.d.ts +23 -0
- package/out/primitives/Box.luau +103 -0
- package/out/primitives/Button.d.ts +62 -0
- package/out/primitives/Button.luau +170 -0
- package/out/primitives/Image.d.ts +37 -0
- package/out/primitives/Image.luau +79 -0
- package/out/primitives/InlineText.d.ts +25 -0
- package/out/primitives/InlineText.luau +273 -0
- package/out/primitives/Input.d.ts +59 -0
- package/out/primitives/Input.luau +126 -0
- package/out/primitives/MotionBox.d.ts +15 -0
- package/out/primitives/MotionBox.luau +69 -0
- package/out/primitives/MotionButton.d.ts +15 -0
- package/out/primitives/MotionButton.luau +146 -0
- package/out/primitives/MotionImage.d.ts +13 -0
- package/out/primitives/MotionImage.luau +70 -0
- package/out/primitives/MotionText.d.ts +12 -0
- package/out/primitives/MotionText.luau +116 -0
- package/out/primitives/MotionUIScale.d.ts +9 -0
- package/out/primitives/MotionUIScale.luau +48 -0
- package/out/primitives/ScrollBox.d.ts +25 -0
- package/out/primitives/ScrollBox.luau +69 -0
- package/out/primitives/Text.d.ts +50 -0
- package/out/primitives/Text.luau +139 -0
- package/out/primitives/usePercentageConstraints.d.ts +3 -0
- package/out/primitives/usePercentageConstraints.luau +112 -0
- package/out/primitives/useVariantResolver.d.ts +13 -0
- package/out/primitives/useVariantResolver.luau +260 -0
- package/out/styles/CSSTypes.d.ts +96 -0
- package/out/styles/ParentSizeContext.d.ts +6 -0
- package/out/styles/ParentSizeContext.luau +13 -0
- package/out/styles/colorParser.d.ts +28 -0
- package/out/styles/colorParser.luau +229 -0
- package/out/styles/dimensionParser.d.ts +49 -0
- package/out/styles/dimensionParser.luau +205 -0
- package/out/styles/gradientParser.d.ts +9 -0
- package/out/styles/gradientParser.luau +434 -0
- package/out/styles/namedColors.d.ts +7 -0
- package/out/styles/namedColors.luau +162 -0
- package/out/styles/transitions.d.ts +18 -0
- package/out/styles/transitions.luau +19 -0
- package/out/styles/webStyle.d.ts +74 -0
- package/out/styles/webStyle.luau +973 -0
- package/out/types.d.ts +4 -0
- package/out/types.luau +3 -0
- package/out/utils/parseInlineImages.d.ts +20 -0
- package/out/utils/parseInlineImages.luau +93 -0
- 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
|
+
[](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.
|
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
|
package/out/logger.d.ts
ADDED
|
@@ -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
|
+
};
|
package/out/logger.luau
ADDED
|
@@ -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>>;
|