@lattice-ui/style 0.1.1

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/out/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export { Box } from "./primitives/Box";
2
+ export { Text } from "./primitives/Text";
3
+ export type { RecipeConfig, RecipeSelection, RecipeVariants } from "./recipe/createRecipe";
4
+ export { createRecipe } from "./recipe/createRecipe";
5
+ export { mergeGuiProps } from "./sx/mergeGuiProps";
6
+ export type { Sx } from "./sx/sx";
7
+ export { mergeSx, resolveSx } from "./sx/sx";
8
+ export { ThemeProvider, useTheme, useThemeValue } from "./theme/ThemeProvider";
9
+ export { createTheme, defaultDarkTheme, defaultLightTheme } from "./theme/tokens";
10
+ export type { PartialTheme, Theme, ThemeColors, ThemeContextValue, ThemeProviderProps, ThemeRadius, ThemeSpace, ThemeTypography, ThemeTypographyStyle, } from "./theme/types";
package/out/init.luau ADDED
@@ -0,0 +1,19 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local exports = {}
4
+ exports.Box = TS.import(script, script, "primitives", "Box").Box
5
+ exports.Text = TS.import(script, script, "primitives", "Text").Text
6
+ exports.createRecipe = TS.import(script, script, "recipe", "createRecipe").createRecipe
7
+ exports.mergeGuiProps = TS.import(script, script, "sx", "mergeGuiProps").mergeGuiProps
8
+ local _sx = TS.import(script, script, "sx", "sx")
9
+ exports.mergeSx = _sx.mergeSx
10
+ exports.resolveSx = _sx.resolveSx
11
+ local _ThemeProvider = TS.import(script, script, "theme", "ThemeProvider")
12
+ exports.ThemeProvider = _ThemeProvider.ThemeProvider
13
+ exports.useTheme = _ThemeProvider.useTheme
14
+ exports.useThemeValue = _ThemeProvider.useThemeValue
15
+ local _tokens = TS.import(script, script, "theme", "tokens")
16
+ exports.createTheme = _tokens.createTheme
17
+ exports.defaultDarkTheme = _tokens.defaultDarkTheme
18
+ exports.defaultLightTheme = _tokens.defaultLightTheme
19
+ return exports
@@ -0,0 +1,10 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { Sx } from "../sx/sx";
3
+ type StyleProps = React.Attributes & Record<string, unknown>;
4
+ export type BoxProps = {
5
+ asChild?: boolean;
6
+ sx?: Sx<StyleProps>;
7
+ children?: React.ReactNode;
8
+ } & StyleProps;
9
+ export declare function Box(props: BoxProps): React.JSX.Element;
10
+ export {};
@@ -0,0 +1,38 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local React = _core.React
5
+ local Slot = _core.Slot
6
+ local mergeGuiProps = TS.import(script, script.Parent.Parent, "sx", "mergeGuiProps").mergeGuiProps
7
+ local resolveSx = TS.import(script, script.Parent.Parent, "sx", "sx").resolveSx
8
+ local useTheme = TS.import(script, script.Parent.Parent, "theme", "ThemeProvider").useTheme
9
+ local function Box(props)
10
+ local asChild = props.asChild
11
+ local sx = props.sx
12
+ local children = props.children
13
+ local restProps = {}
14
+ for rawKey, value in pairs(props) do
15
+ if not (type(rawKey) == "string") then
16
+ continue
17
+ end
18
+ if rawKey == "asChild" or rawKey == "sx" or rawKey == "children" then
19
+ continue
20
+ end
21
+ restProps[rawKey] = value
22
+ end
23
+ local _binding = useTheme()
24
+ local theme = _binding.theme
25
+ local mergedProps = mergeGuiProps(restProps, resolveSx(sx, theme))
26
+ if asChild then
27
+ if not React.isValidElement(children) then
28
+ error("[Box] `asChild` requires a single child element.")
29
+ end
30
+ local _attributes = table.clone(mergedProps)
31
+ setmetatable(_attributes, nil)
32
+ return React.createElement(Slot, _attributes, children)
33
+ end
34
+ return React.createElement("frame", mergedProps, children)
35
+ end
36
+ return {
37
+ Box = Box,
38
+ }
@@ -0,0 +1,10 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { Sx } from "../sx/sx";
3
+ type StyleProps = React.Attributes & Record<string, unknown>;
4
+ export type TextProps = {
5
+ asChild?: boolean;
6
+ sx?: Sx<StyleProps>;
7
+ children?: React.ReactNode;
8
+ } & StyleProps;
9
+ export declare function Text(props: TextProps): React.JSX.Element;
10
+ export {};
@@ -0,0 +1,38 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local React = _core.React
5
+ local Slot = _core.Slot
6
+ local mergeGuiProps = TS.import(script, script.Parent.Parent, "sx", "mergeGuiProps").mergeGuiProps
7
+ local resolveSx = TS.import(script, script.Parent.Parent, "sx", "sx").resolveSx
8
+ local useTheme = TS.import(script, script.Parent.Parent, "theme", "ThemeProvider").useTheme
9
+ local function Text(props)
10
+ local asChild = props.asChild
11
+ local sx = props.sx
12
+ local children = props.children
13
+ local restProps = {}
14
+ for rawKey, value in pairs(props) do
15
+ if not (type(rawKey) == "string") then
16
+ continue
17
+ end
18
+ if rawKey == "asChild" or rawKey == "sx" or rawKey == "children" then
19
+ continue
20
+ end
21
+ restProps[rawKey] = value
22
+ end
23
+ local _binding = useTheme()
24
+ local theme = _binding.theme
25
+ local mergedProps = mergeGuiProps(restProps, resolveSx(sx, theme))
26
+ if asChild then
27
+ if not React.isValidElement(children) then
28
+ error("[Text] `asChild` requires a single child element.")
29
+ end
30
+ local _attributes = table.clone(mergedProps)
31
+ setmetatable(_attributes, nil)
32
+ return React.createElement(Slot, _attributes, children)
33
+ end
34
+ return React.createElement("textlabel", mergedProps, children)
35
+ end
36
+ return {
37
+ Text = Text,
38
+ }
@@ -0,0 +1,16 @@
1
+ import type { Sx } from "../sx/sx";
2
+ import type { Theme } from "../theme/types";
3
+ type GuiPropRecord = Record<string, unknown>;
4
+ export type RecipeVariants<Props extends GuiPropRecord> = Record<string, Record<string, Sx<Props>>>;
5
+ export type RecipeSelection<Variants extends RecipeVariants<GuiPropRecord>> = Partial<Record<keyof Variants & string, string>>;
6
+ export type RecipeConfig<Props extends GuiPropRecord, Variants extends RecipeVariants<Props>> = {
7
+ base?: Sx<Props>;
8
+ variants?: Variants;
9
+ defaultVariants?: RecipeSelection<Variants>;
10
+ compoundVariants?: Array<{
11
+ variants: RecipeSelection<Variants>;
12
+ sx: Sx<Props>;
13
+ }>;
14
+ };
15
+ export declare function createRecipe<Props extends GuiPropRecord, Variants extends RecipeVariants<Props>>(config: RecipeConfig<Props, Variants>): (selection: RecipeSelection<Variants> | undefined, theme: Theme) => Partial<Props>;
16
+ export {};
@@ -0,0 +1,63 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local mergeGuiProps = TS.import(script, script.Parent.Parent, "sx", "mergeGuiProps").mergeGuiProps
4
+ local resolveSx = TS.import(script, script.Parent.Parent, "sx", "sx").resolveSx
5
+ local function isCompoundMatch(candidate, resolvedSelection)
6
+ local candidateRecord = candidate
7
+ local resolvedRecord = resolvedSelection
8
+ for rawVariantName, rawExpectedValue in pairs(candidateRecord) do
9
+ if not (type(rawVariantName) == "string") or not (type(rawExpectedValue) == "string") then
10
+ continue
11
+ end
12
+ local actualValue = resolvedRecord[rawVariantName]
13
+ if actualValue ~= rawExpectedValue then
14
+ return false
15
+ end
16
+ end
17
+ return true
18
+ end
19
+ local function createRecipe(config)
20
+ return function(selection, theme)
21
+ local _condition = config.defaultVariants
22
+ if _condition == nil then
23
+ _condition = {}
24
+ end
25
+ local _object = table.clone(_condition)
26
+ setmetatable(_object, nil)
27
+ local _condition_1 = selection
28
+ if _condition_1 == nil then
29
+ _condition_1 = {}
30
+ end
31
+ for _k, _v in _condition_1 do
32
+ _object[_k] = _v
33
+ end
34
+ local resolvedSelection = _object
35
+ local merged = resolveSx(config.base, theme)
36
+ local variants = config.variants
37
+ if variants then
38
+ local variantsRecord = variants
39
+ local resolvedRecord = resolvedSelection
40
+ for rawVariantName, rawVariantMap in pairs(variantsRecord) do
41
+ if not (type(rawVariantName) == "string") or not (type(rawVariantMap) == "table") then
42
+ continue
43
+ end
44
+ local selectedValue = resolvedRecord[rawVariantName]
45
+ if selectedValue == nil then
46
+ continue
47
+ end
48
+ local sx = rawVariantMap[selectedValue]
49
+ merged = mergeGuiProps(merged, resolveSx(sx, theme))
50
+ end
51
+ end
52
+ for _, compound in config.compoundVariants or {} do
53
+ if not isCompoundMatch(compound.variants, resolvedSelection) then
54
+ continue
55
+ end
56
+ merged = mergeGuiProps(merged, resolveSx(compound.sx, theme))
57
+ end
58
+ return merged
59
+ end
60
+ end
61
+ return {
62
+ createRecipe = createRecipe,
63
+ }
@@ -0,0 +1,3 @@
1
+ type GuiPropRecord = Record<string, unknown>;
2
+ export declare function mergeGuiProps<Props extends GuiPropRecord>(base?: Partial<Props>, variant?: Partial<Props>, user?: Partial<Props>): Partial<Props>;
3
+ export {};
@@ -0,0 +1,105 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local function isRecord(value)
3
+ local _value = value
4
+ return type(_value) == "table"
5
+ end
6
+ local function isFn(value)
7
+ local _value = value
8
+ return type(_value) == "function"
9
+ end
10
+ local function toHandlerTable(value)
11
+ if not isRecord(value) then
12
+ return nil
13
+ end
14
+ local out = {}
15
+ for rawKey, candidate in pairs(value) do
16
+ if not (type(rawKey) == "string") or not isFn(candidate) then
17
+ continue
18
+ end
19
+ out[rawKey] = candidate
20
+ end
21
+ return if (next(out)) ~= nil then out else nil
22
+ end
23
+ local function mergeHandlerTables(tables)
24
+ local out = {}
25
+ for _, handlerTable in tables do
26
+ if not handlerTable then
27
+ continue
28
+ end
29
+ for rawKey, candidate in pairs(handlerTable) do
30
+ if not (type(rawKey) == "string") or not isFn(candidate) then
31
+ continue
32
+ end
33
+ local previous = out[rawKey]
34
+ out[rawKey] = if previous ~= nil then function(...)
35
+ local args = { ... }
36
+ previous(unpack(args))
37
+ candidate(unpack(args))
38
+ end else candidate
39
+ end
40
+ end
41
+ return if (next(out)) ~= nil then out else nil
42
+ end
43
+ local function mergeGuiProps(base, variant, user)
44
+ local _condition = base
45
+ if _condition == nil then
46
+ _condition = {}
47
+ end
48
+ local _object = table.clone(_condition)
49
+ setmetatable(_object, nil)
50
+ local _condition_1 = variant
51
+ if _condition_1 == nil then
52
+ _condition_1 = {}
53
+ end
54
+ for _k, _v in _condition_1 do
55
+ _object[_k] = _v
56
+ end
57
+ local _condition_2 = user
58
+ if _condition_2 == nil then
59
+ _condition_2 = {}
60
+ end
61
+ for _k, _v in _condition_2 do
62
+ _object[_k] = _v
63
+ end
64
+ local merged = _object
65
+ local _result = base
66
+ if _result ~= nil then
67
+ _result = _result.Event
68
+ end
69
+ local _exp = toHandlerTable(_result)
70
+ local _result_1 = variant
71
+ if _result_1 ~= nil then
72
+ _result_1 = _result_1.Event
73
+ end
74
+ local _exp_1 = toHandlerTable(_result_1)
75
+ local _result_2 = user
76
+ if _result_2 ~= nil then
77
+ _result_2 = _result_2.Event
78
+ end
79
+ local mergedEvent = mergeHandlerTables({ _exp, _exp_1, toHandlerTable(_result_2) })
80
+ if mergedEvent then
81
+ merged.Event = mergedEvent
82
+ end
83
+ local _result_3 = base
84
+ if _result_3 ~= nil then
85
+ _result_3 = _result_3.Change
86
+ end
87
+ local _exp_2 = toHandlerTable(_result_3)
88
+ local _result_4 = variant
89
+ if _result_4 ~= nil then
90
+ _result_4 = _result_4.Change
91
+ end
92
+ local _exp_3 = toHandlerTable(_result_4)
93
+ local _result_5 = user
94
+ if _result_5 ~= nil then
95
+ _result_5 = _result_5.Change
96
+ end
97
+ local mergedChange = mergeHandlerTables({ _exp_2, _exp_3, toHandlerTable(_result_5) })
98
+ if mergedChange then
99
+ merged.Change = mergedChange
100
+ end
101
+ return merged
102
+ end
103
+ return {
104
+ mergeGuiProps = mergeGuiProps,
105
+ }
package/out/sx/sx.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { Theme } from "../theme/types";
2
+ type GuiPropRecord = Record<string, unknown>;
3
+ export type Sx<Props extends GuiPropRecord> = Partial<Props> | ((theme: Theme) => Partial<Props>) | undefined;
4
+ export declare function resolveSx<Props extends GuiPropRecord>(sx: Sx<Props>, theme: Theme): Partial<Props>;
5
+ export declare function mergeSx<Props extends GuiPropRecord>(...sxList: Array<Sx<Props>>): Sx<Props>;
6
+ export {};
package/out/sx/sx.luau ADDED
@@ -0,0 +1,31 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local mergeGuiProps = TS.import(script, script.Parent, "mergeGuiProps").mergeGuiProps
4
+ local function isSxResolver(sx)
5
+ local _sx = sx
6
+ return type(_sx) == "function"
7
+ end
8
+ local function resolveSx(sx, theme)
9
+ if not sx then
10
+ return {}
11
+ end
12
+ if isSxResolver(sx) then
13
+ return sx(theme)
14
+ end
15
+ return sx
16
+ end
17
+ local function mergeSx(...)
18
+ local sxList = { ... }
19
+ return function(theme)
20
+ local merged = {}
21
+ for _, sx in sxList do
22
+ local resolved = resolveSx(sx, theme)
23
+ merged = mergeGuiProps(merged, resolved)
24
+ end
25
+ return merged
26
+ end
27
+ end
28
+ return {
29
+ resolveSx = resolveSx,
30
+ mergeSx = mergeSx,
31
+ }
@@ -0,0 +1,5 @@
1
+ import { React } from "@lattice-ui/core";
2
+ import type { Theme, ThemeContextValue, ThemeProviderProps } from "./types";
3
+ export declare function ThemeProvider(props: ThemeProviderProps): React.JSX.Element;
4
+ export declare function useTheme(): ThemeContextValue;
5
+ export declare function useThemeValue<T>(selector: (theme: Theme) => T): T;
@@ -0,0 +1,46 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _core = TS.import(script, TS.getModule(script, "@lattice-ui", "core").out)
4
+ local createStrictContext = _core.createStrictContext
5
+ local React = _core.React
6
+ local defaultLightTheme = TS.import(script, script.Parent, "tokens").defaultLightTheme
7
+ local _binding = createStrictContext("ThemeProvider")
8
+ local ThemeContextProvider = _binding[1]
9
+ local useThemeContext = _binding[2]
10
+ local function ThemeProvider(props)
11
+ local internalTheme, setInternalTheme = React.useState(props.defaultTheme or defaultLightTheme)
12
+ local controlled = props.theme ~= nil
13
+ local resolvedTheme = props.theme or internalTheme
14
+ local setTheme = React.useCallback(function(nextTheme)
15
+ if not controlled then
16
+ setInternalTheme(nextTheme)
17
+ end
18
+ local _result = props.onThemeChange
19
+ if _result ~= nil then
20
+ _result(nextTheme)
21
+ end
22
+ end, { controlled, props.onThemeChange })
23
+ local contextValue = React.useMemo(function()
24
+ return {
25
+ theme = resolvedTheme,
26
+ setTheme = setTheme,
27
+ }
28
+ end, { resolvedTheme, setTheme })
29
+ return React.createElement(ThemeContextProvider, {
30
+ value = contextValue,
31
+ }, props.children)
32
+ end
33
+ local function useTheme()
34
+ return useThemeContext()
35
+ end
36
+ local function useThemeValue(selector)
37
+ local context = useThemeContext()
38
+ return React.useMemo(function()
39
+ return selector(context.theme)
40
+ end, { context.theme, selector })
41
+ end
42
+ return {
43
+ ThemeProvider = ThemeProvider,
44
+ useTheme = useTheme,
45
+ useThemeValue = useThemeValue,
46
+ }
@@ -0,0 +1,4 @@
1
+ import type { PartialTheme, Theme } from "./types";
2
+ export declare const defaultLightTheme: Theme;
3
+ export declare const defaultDarkTheme: Theme;
4
+ export declare function createTheme(partialTheme?: PartialTheme): Theme;
@@ -0,0 +1,133 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local defaultSpace = {
3
+ [0] = 0,
4
+ [2] = 2,
5
+ [4] = 4,
6
+ [6] = 6,
7
+ [8] = 8,
8
+ [10] = 10,
9
+ [12] = 12,
10
+ [14] = 14,
11
+ [16] = 16,
12
+ [20] = 20,
13
+ [24] = 24,
14
+ [32] = 32,
15
+ }
16
+ local defaultRadius = {
17
+ none = 0,
18
+ sm = 4,
19
+ md = 8,
20
+ lg = 12,
21
+ xl = 16,
22
+ full = 999,
23
+ }
24
+ local defaultTypography = {
25
+ labelSm = {
26
+ font = Enum.Font.Gotham,
27
+ textSize = 14,
28
+ },
29
+ bodyMd = {
30
+ font = Enum.Font.Gotham,
31
+ textSize = 16,
32
+ },
33
+ titleMd = {
34
+ font = Enum.Font.GothamBold,
35
+ textSize = 22,
36
+ },
37
+ }
38
+ local defaultLightTheme = {
39
+ colors = {
40
+ background = Color3.fromRGB(246, 249, 252),
41
+ surface = Color3.fromRGB(233, 239, 246),
42
+ surfaceElevated = Color3.fromRGB(255, 255, 255),
43
+ border = Color3.fromRGB(193, 202, 214),
44
+ textPrimary = Color3.fromRGB(27, 33, 44),
45
+ textSecondary = Color3.fromRGB(79, 90, 107),
46
+ accent = Color3.fromRGB(46, 114, 216),
47
+ accentContrast = Color3.fromRGB(239, 245, 252),
48
+ danger = Color3.fromRGB(167, 56, 64),
49
+ dangerContrast = Color3.fromRGB(254, 236, 238),
50
+ overlay = Color3.fromRGB(12, 16, 23),
51
+ },
52
+ space = defaultSpace,
53
+ radius = defaultRadius,
54
+ typography = defaultTypography,
55
+ }
56
+ local defaultDarkTheme = {
57
+ colors = {
58
+ background = Color3.fromRGB(18, 21, 26),
59
+ surface = Color3.fromRGB(32, 37, 46),
60
+ surfaceElevated = Color3.fromRGB(40, 47, 60),
61
+ border = Color3.fromRGB(72, 80, 98),
62
+ textPrimary = Color3.fromRGB(233, 239, 246),
63
+ textSecondary = Color3.fromRGB(176, 186, 201),
64
+ accent = Color3.fromRGB(43, 105, 196),
65
+ accentContrast = Color3.fromRGB(240, 244, 250),
66
+ danger = Color3.fromRGB(129, 57, 63),
67
+ dangerContrast = Color3.fromRGB(245, 223, 226),
68
+ overlay = Color3.fromRGB(8, 10, 14),
69
+ },
70
+ space = defaultSpace,
71
+ radius = defaultRadius,
72
+ typography = defaultTypography,
73
+ }
74
+ local function mergeTheme(baseTheme, partialTheme)
75
+ if not partialTheme then
76
+ local _object = {}
77
+ local _left = "colors"
78
+ local _object_1 = table.clone(baseTheme.colors)
79
+ setmetatable(_object_1, nil)
80
+ _object[_left] = _object_1
81
+ local _left_1 = "space"
82
+ local _object_2 = table.clone(baseTheme.space)
83
+ setmetatable(_object_2, nil)
84
+ _object[_left_1] = _object_2
85
+ local _left_2 = "radius"
86
+ local _object_3 = table.clone(baseTheme.radius)
87
+ setmetatable(_object_3, nil)
88
+ _object[_left_2] = _object_3
89
+ local _left_3 = "typography"
90
+ local _object_4 = table.clone(baseTheme.typography)
91
+ setmetatable(_object_4, nil)
92
+ _object[_left_3] = _object_4
93
+ return _object
94
+ end
95
+ local _object = {}
96
+ local _left = "colors"
97
+ local _object_1 = table.clone(baseTheme.colors)
98
+ setmetatable(_object_1, nil)
99
+ for _k, _v in (partialTheme.colors or {}) do
100
+ _object_1[_k] = _v
101
+ end
102
+ _object[_left] = _object_1
103
+ local _left_1 = "space"
104
+ local _object_2 = table.clone(baseTheme.space)
105
+ setmetatable(_object_2, nil)
106
+ for _k, _v in (partialTheme.space or {}) do
107
+ _object_2[_k] = _v
108
+ end
109
+ _object[_left_1] = _object_2
110
+ local _left_2 = "radius"
111
+ local _object_3 = table.clone(baseTheme.radius)
112
+ setmetatable(_object_3, nil)
113
+ for _k, _v in (partialTheme.radius or {}) do
114
+ _object_3[_k] = _v
115
+ end
116
+ _object[_left_2] = _object_3
117
+ local _left_3 = "typography"
118
+ local _object_4 = table.clone(baseTheme.typography)
119
+ setmetatable(_object_4, nil)
120
+ for _k, _v in (partialTheme.typography or {}) do
121
+ _object_4[_k] = _v
122
+ end
123
+ _object[_left_3] = _object_4
124
+ return _object
125
+ end
126
+ local function createTheme(partialTheme)
127
+ return mergeTheme(defaultLightTheme, partialTheme)
128
+ end
129
+ return {
130
+ createTheme = createTheme,
131
+ defaultLightTheme = defaultLightTheme,
132
+ defaultDarkTheme = defaultDarkTheme,
133
+ }
@@ -0,0 +1,67 @@
1
+ import type React from "@rbxts/react";
2
+ export type ThemeColors = {
3
+ background: Color3;
4
+ surface: Color3;
5
+ surfaceElevated: Color3;
6
+ border: Color3;
7
+ textPrimary: Color3;
8
+ textSecondary: Color3;
9
+ accent: Color3;
10
+ accentContrast: Color3;
11
+ danger: Color3;
12
+ dangerContrast: Color3;
13
+ overlay: Color3;
14
+ };
15
+ export type ThemeSpace = {
16
+ 0: number;
17
+ 2: number;
18
+ 4: number;
19
+ 6: number;
20
+ 8: number;
21
+ 10: number;
22
+ 12: number;
23
+ 14: number;
24
+ 16: number;
25
+ 20: number;
26
+ 24: number;
27
+ 32: number;
28
+ };
29
+ export type ThemeRadius = {
30
+ none: number;
31
+ sm: number;
32
+ md: number;
33
+ lg: number;
34
+ xl: number;
35
+ full: number;
36
+ };
37
+ export type ThemeTypographyStyle = {
38
+ font: Enum.Font;
39
+ textSize: number;
40
+ };
41
+ export type ThemeTypography = {
42
+ labelSm: ThemeTypographyStyle;
43
+ bodyMd: ThemeTypographyStyle;
44
+ titleMd: ThemeTypographyStyle;
45
+ };
46
+ export type Theme = {
47
+ colors: ThemeColors;
48
+ space: ThemeSpace;
49
+ radius: ThemeRadius;
50
+ typography: ThemeTypography;
51
+ };
52
+ export type PartialTheme = {
53
+ colors?: Partial<ThemeColors>;
54
+ space?: Partial<ThemeSpace>;
55
+ radius?: Partial<ThemeRadius>;
56
+ typography?: Partial<ThemeTypography>;
57
+ };
58
+ export type ThemeContextValue = {
59
+ theme: Theme;
60
+ setTheme: (nextTheme: Theme) => void;
61
+ };
62
+ export type ThemeProviderProps = {
63
+ theme?: Theme;
64
+ defaultTheme?: Theme;
65
+ onThemeChange?: (nextTheme: Theme) => void;
66
+ children?: React.ReactNode;
67
+ };
@@ -0,0 +1,2 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ return nil
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@lattice-ui/style",
3
+ "version": "0.1.1",
4
+ "private": false,
5
+ "main": "out/init.luau",
6
+ "types": "out/index.d.ts",
7
+ "dependencies": {
8
+ "@lattice-ui/core": "0.1.1"
9
+ },
10
+ "devDependencies": {
11
+ "@rbxts/react": "17.3.7-ts.1",
12
+ "@rbxts/react-roblox": "17.3.7-ts.1"
13
+ },
14
+ "peerDependencies": {
15
+ "@rbxts/react": "^17",
16
+ "@rbxts/react-roblox": "^17"
17
+ },
18
+ "scripts": {
19
+ "build": "rbxtsc -p tsconfig.json",
20
+ "watch": "rbxtsc -p tsconfig.json -w",
21
+ "typecheck": "tsc -p tsconfig.typecheck.json"
22
+ }
23
+ }
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ export { Box } from "./primitives/Box";
2
+ export { Text } from "./primitives/Text";
3
+ export type { RecipeConfig, RecipeSelection, RecipeVariants } from "./recipe/createRecipe";
4
+ export { createRecipe } from "./recipe/createRecipe";
5
+ export { mergeGuiProps } from "./sx/mergeGuiProps";
6
+ export type { Sx } from "./sx/sx";
7
+ export { mergeSx, resolveSx } from "./sx/sx";
8
+ export { ThemeProvider, useTheme, useThemeValue } from "./theme/ThemeProvider";
9
+ export { createTheme, defaultDarkTheme, defaultLightTheme } from "./theme/tokens";
10
+ export type {
11
+ PartialTheme,
12
+ Theme,
13
+ ThemeColors,
14
+ ThemeContextValue,
15
+ ThemeProviderProps,
16
+ ThemeRadius,
17
+ ThemeSpace,
18
+ ThemeTypography,
19
+ ThemeTypographyStyle,
20
+ } from "./theme/types";
@@ -0,0 +1,44 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { mergeGuiProps } from "../sx/mergeGuiProps";
3
+ import type { Sx } from "../sx/sx";
4
+ import { resolveSx } from "../sx/sx";
5
+ import { useTheme } from "../theme/ThemeProvider";
6
+
7
+ type StyleProps = React.Attributes & Record<string, unknown>;
8
+
9
+ export type BoxProps = {
10
+ asChild?: boolean;
11
+ sx?: Sx<StyleProps>;
12
+ children?: React.ReactNode;
13
+ } & StyleProps;
14
+
15
+ export function Box(props: BoxProps) {
16
+ const asChild = props.asChild;
17
+ const sx = props.sx;
18
+ const children = props.children;
19
+ const restProps: StyleProps = {};
20
+ for (const [rawKey, value] of pairs(props as Record<string, unknown>)) {
21
+ if (!typeIs(rawKey, "string")) {
22
+ continue;
23
+ }
24
+
25
+ if (rawKey === "asChild" || rawKey === "sx" || rawKey === "children") {
26
+ continue;
27
+ }
28
+
29
+ restProps[rawKey] = value;
30
+ }
31
+
32
+ const { theme } = useTheme();
33
+ const mergedProps = mergeGuiProps(restProps, resolveSx(sx, theme));
34
+
35
+ if (asChild) {
36
+ if (!React.isValidElement(children)) {
37
+ error("[Box] `asChild` requires a single child element.");
38
+ }
39
+
40
+ return <Slot {...mergedProps}>{children}</Slot>;
41
+ }
42
+
43
+ return React.createElement("frame", mergedProps as never, children);
44
+ }
@@ -0,0 +1,44 @@
1
+ import { React, Slot } from "@lattice-ui/core";
2
+ import { mergeGuiProps } from "../sx/mergeGuiProps";
3
+ import type { Sx } from "../sx/sx";
4
+ import { resolveSx } from "../sx/sx";
5
+ import { useTheme } from "../theme/ThemeProvider";
6
+
7
+ type StyleProps = React.Attributes & Record<string, unknown>;
8
+
9
+ export type TextProps = {
10
+ asChild?: boolean;
11
+ sx?: Sx<StyleProps>;
12
+ children?: React.ReactNode;
13
+ } & StyleProps;
14
+
15
+ export function Text(props: TextProps) {
16
+ const asChild = props.asChild;
17
+ const sx = props.sx;
18
+ const children = props.children;
19
+ const restProps: StyleProps = {};
20
+ for (const [rawKey, value] of pairs(props as Record<string, unknown>)) {
21
+ if (!typeIs(rawKey, "string")) {
22
+ continue;
23
+ }
24
+
25
+ if (rawKey === "asChild" || rawKey === "sx" || rawKey === "children") {
26
+ continue;
27
+ }
28
+
29
+ restProps[rawKey] = value;
30
+ }
31
+
32
+ const { theme } = useTheme();
33
+ const mergedProps = mergeGuiProps(restProps, resolveSx(sx, theme));
34
+
35
+ if (asChild) {
36
+ if (!React.isValidElement(children)) {
37
+ error("[Text] `asChild` requires a single child element.");
38
+ }
39
+
40
+ return <Slot {...mergedProps}>{children}</Slot>;
41
+ }
42
+
43
+ return React.createElement("textlabel", mergedProps as never, children);
44
+ }
@@ -0,0 +1,85 @@
1
+ import { mergeGuiProps } from "../sx/mergeGuiProps";
2
+ import type { Sx } from "../sx/sx";
3
+ import { resolveSx } from "../sx/sx";
4
+ import type { Theme } from "../theme/types";
5
+
6
+ type GuiPropRecord = Record<string, unknown>;
7
+
8
+ export type RecipeVariants<Props extends GuiPropRecord> = Record<string, Record<string, Sx<Props>>>;
9
+ export type RecipeSelection<Variants extends RecipeVariants<GuiPropRecord>> = Partial<
10
+ Record<keyof Variants & string, string>
11
+ >;
12
+
13
+ export type RecipeConfig<Props extends GuiPropRecord, Variants extends RecipeVariants<Props>> = {
14
+ base?: Sx<Props>;
15
+ variants?: Variants;
16
+ defaultVariants?: RecipeSelection<Variants>;
17
+ compoundVariants?: Array<{
18
+ variants: RecipeSelection<Variants>;
19
+ sx: Sx<Props>;
20
+ }>;
21
+ };
22
+
23
+ function isCompoundMatch<Variants extends RecipeVariants<GuiPropRecord>>(
24
+ candidate: RecipeSelection<Variants>,
25
+ resolvedSelection: RecipeSelection<Variants>,
26
+ ) {
27
+ const candidateRecord = candidate as Record<string, string | undefined>;
28
+ const resolvedRecord = resolvedSelection as Record<string, string | undefined>;
29
+
30
+ for (const [rawVariantName, rawExpectedValue] of pairs(candidateRecord)) {
31
+ if (!typeIs(rawVariantName, "string") || !typeIs(rawExpectedValue, "string")) {
32
+ continue;
33
+ }
34
+
35
+ const actualValue = resolvedRecord[rawVariantName];
36
+ if (actualValue !== rawExpectedValue) {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ return true;
42
+ }
43
+
44
+ export function createRecipe<Props extends GuiPropRecord, Variants extends RecipeVariants<Props>>(
45
+ config: RecipeConfig<Props, Variants>,
46
+ ) {
47
+ return (selection: RecipeSelection<Variants> | undefined, theme: Theme): Partial<Props> => {
48
+ const resolvedSelection = {
49
+ ...(config.defaultVariants ?? {}),
50
+ ...(selection ?? {}),
51
+ } as RecipeSelection<Variants>;
52
+
53
+ let merged = resolveSx(config.base, theme);
54
+
55
+ const variants = config.variants;
56
+ if (variants) {
57
+ const variantsRecord = variants as Record<string, Record<string, Sx<Props>>>;
58
+ const resolvedRecord = resolvedSelection as Record<string, string | undefined>;
59
+
60
+ for (const [rawVariantName, rawVariantMap] of pairs(variantsRecord)) {
61
+ if (!typeIs(rawVariantName, "string") || !typeIs(rawVariantMap, "table")) {
62
+ continue;
63
+ }
64
+
65
+ const selectedValue = resolvedRecord[rawVariantName];
66
+ if (selectedValue === undefined) {
67
+ continue;
68
+ }
69
+
70
+ const sx = rawVariantMap[selectedValue];
71
+ merged = mergeGuiProps(merged, resolveSx(sx, theme));
72
+ }
73
+ }
74
+
75
+ for (const compound of config.compoundVariants ?? []) {
76
+ if (!isCompoundMatch(compound.variants, resolvedSelection)) {
77
+ continue;
78
+ }
79
+
80
+ merged = mergeGuiProps(merged, resolveSx(compound.sx, theme));
81
+ }
82
+
83
+ return merged;
84
+ };
85
+ }
@@ -0,0 +1,87 @@
1
+ type GuiHandler = (...args: unknown[]) => void;
2
+ type GuiHandlerTable = Partial<Record<string, GuiHandler>>;
3
+ type GuiPropRecord = Record<string, unknown>;
4
+
5
+ function isRecord(value: unknown): value is GuiPropRecord {
6
+ return typeIs(value, "table");
7
+ }
8
+
9
+ function isFn(value: unknown): value is GuiHandler {
10
+ return typeIs(value, "function");
11
+ }
12
+
13
+ function toHandlerTable(value: unknown): GuiHandlerTable | undefined {
14
+ if (!isRecord(value)) {
15
+ return undefined;
16
+ }
17
+
18
+ const out: GuiHandlerTable = {};
19
+ for (const [rawKey, candidate] of pairs(value)) {
20
+ if (!typeIs(rawKey, "string") || !isFn(candidate)) {
21
+ continue;
22
+ }
23
+
24
+ out[rawKey] = candidate;
25
+ }
26
+
27
+ return next(out)[0] !== undefined ? out : undefined;
28
+ }
29
+
30
+ function mergeHandlerTables(tables: Array<GuiHandlerTable | undefined>): GuiHandlerTable | undefined {
31
+ const out: GuiHandlerTable = {};
32
+
33
+ for (const handlerTable of tables) {
34
+ if (!handlerTable) {
35
+ continue;
36
+ }
37
+
38
+ for (const [rawKey, candidate] of pairs(handlerTable)) {
39
+ if (!typeIs(rawKey, "string") || !isFn(candidate)) {
40
+ continue;
41
+ }
42
+
43
+ const previous = out[rawKey];
44
+ out[rawKey] =
45
+ previous !== undefined
46
+ ? (...args: unknown[]) => {
47
+ previous(...args);
48
+ candidate(...args);
49
+ }
50
+ : candidate;
51
+ }
52
+ }
53
+
54
+ return next(out)[0] !== undefined ? out : undefined;
55
+ }
56
+
57
+ export function mergeGuiProps<Props extends GuiPropRecord>(
58
+ base?: Partial<Props>,
59
+ variant?: Partial<Props>,
60
+ user?: Partial<Props>,
61
+ ): Partial<Props> {
62
+ const merged = {
63
+ ...(base ?? {}),
64
+ ...(variant ?? {}),
65
+ ...(user ?? {}),
66
+ } as Partial<Props>;
67
+
68
+ const mergedEvent = mergeHandlerTables([
69
+ toHandlerTable(base?.Event),
70
+ toHandlerTable(variant?.Event),
71
+ toHandlerTable(user?.Event),
72
+ ]);
73
+ if (mergedEvent) {
74
+ (merged as GuiPropRecord).Event = mergedEvent;
75
+ }
76
+
77
+ const mergedChange = mergeHandlerTables([
78
+ toHandlerTable(base?.Change),
79
+ toHandlerTable(variant?.Change),
80
+ toHandlerTable(user?.Change),
81
+ ]);
82
+ if (mergedChange) {
83
+ (merged as GuiPropRecord).Change = mergedChange;
84
+ }
85
+
86
+ return merged;
87
+ }
package/src/sx/sx.ts ADDED
@@ -0,0 +1,35 @@
1
+ import type { Theme } from "../theme/types";
2
+ import { mergeGuiProps } from "./mergeGuiProps";
3
+
4
+ type GuiPropRecord = Record<string, unknown>;
5
+
6
+ export type Sx<Props extends GuiPropRecord> = Partial<Props> | ((theme: Theme) => Partial<Props>) | undefined;
7
+
8
+ function isSxResolver<Props extends GuiPropRecord>(sx: Sx<Props>): sx is (theme: Theme) => Partial<Props> {
9
+ return typeIs(sx, "function");
10
+ }
11
+
12
+ export function resolveSx<Props extends GuiPropRecord>(sx: Sx<Props>, theme: Theme): Partial<Props> {
13
+ if (!sx) {
14
+ return {};
15
+ }
16
+
17
+ if (isSxResolver(sx)) {
18
+ return sx(theme);
19
+ }
20
+
21
+ return sx;
22
+ }
23
+
24
+ export function mergeSx<Props extends GuiPropRecord>(...sxList: Array<Sx<Props>>): Sx<Props> {
25
+ return (theme) => {
26
+ let merged: Partial<Props> = {};
27
+
28
+ for (const sx of sxList) {
29
+ const resolved = resolveSx(sx, theme);
30
+ merged = mergeGuiProps(merged, resolved);
31
+ }
32
+
33
+ return merged;
34
+ };
35
+ }
@@ -0,0 +1,41 @@
1
+ import { createStrictContext, React } from "@lattice-ui/core";
2
+ import { defaultLightTheme } from "./tokens";
3
+ import type { Theme, ThemeContextValue, ThemeProviderProps } from "./types";
4
+
5
+ const [ThemeContextProvider, useThemeContext] = createStrictContext<ThemeContextValue>("ThemeProvider");
6
+
7
+ export function ThemeProvider(props: ThemeProviderProps) {
8
+ const [internalTheme, setInternalTheme] = React.useState(props.defaultTheme ?? defaultLightTheme);
9
+ const controlled = props.theme !== undefined;
10
+ const resolvedTheme = props.theme ?? internalTheme;
11
+
12
+ const setTheme = React.useCallback(
13
+ (nextTheme: Theme) => {
14
+ if (!controlled) {
15
+ setInternalTheme(nextTheme);
16
+ }
17
+
18
+ props.onThemeChange?.(nextTheme);
19
+ },
20
+ [controlled, props.onThemeChange],
21
+ );
22
+
23
+ const contextValue = React.useMemo(
24
+ () => ({
25
+ theme: resolvedTheme,
26
+ setTheme,
27
+ }),
28
+ [resolvedTheme, setTheme],
29
+ );
30
+
31
+ return <ThemeContextProvider value={contextValue}>{props.children}</ThemeContextProvider>;
32
+ }
33
+
34
+ export function useTheme() {
35
+ return useThemeContext();
36
+ }
37
+
38
+ export function useThemeValue<T>(selector: (theme: Theme) => T): T {
39
+ const context = useThemeContext();
40
+ return React.useMemo(() => selector(context.theme), [context.theme, selector]);
41
+ }
@@ -0,0 +1,112 @@
1
+ import type { PartialTheme, Theme } from "./types";
2
+
3
+ const defaultSpace = {
4
+ 0: 0,
5
+ 2: 2,
6
+ 4: 4,
7
+ 6: 6,
8
+ 8: 8,
9
+ 10: 10,
10
+ 12: 12,
11
+ 14: 14,
12
+ 16: 16,
13
+ 20: 20,
14
+ 24: 24,
15
+ 32: 32,
16
+ };
17
+
18
+ const defaultRadius = {
19
+ none: 0,
20
+ sm: 4,
21
+ md: 8,
22
+ lg: 12,
23
+ xl: 16,
24
+ full: 999,
25
+ };
26
+
27
+ const defaultTypography = {
28
+ labelSm: {
29
+ font: Enum.Font.Gotham,
30
+ textSize: 14,
31
+ },
32
+ bodyMd: {
33
+ font: Enum.Font.Gotham,
34
+ textSize: 16,
35
+ },
36
+ titleMd: {
37
+ font: Enum.Font.GothamBold,
38
+ textSize: 22,
39
+ },
40
+ };
41
+
42
+ export const defaultLightTheme: Theme = {
43
+ colors: {
44
+ background: Color3.fromRGB(246, 249, 252),
45
+ surface: Color3.fromRGB(233, 239, 246),
46
+ surfaceElevated: Color3.fromRGB(255, 255, 255),
47
+ border: Color3.fromRGB(193, 202, 214),
48
+ textPrimary: Color3.fromRGB(27, 33, 44),
49
+ textSecondary: Color3.fromRGB(79, 90, 107),
50
+ accent: Color3.fromRGB(46, 114, 216),
51
+ accentContrast: Color3.fromRGB(239, 245, 252),
52
+ danger: Color3.fromRGB(167, 56, 64),
53
+ dangerContrast: Color3.fromRGB(254, 236, 238),
54
+ overlay: Color3.fromRGB(12, 16, 23),
55
+ },
56
+ space: defaultSpace,
57
+ radius: defaultRadius,
58
+ typography: defaultTypography,
59
+ };
60
+
61
+ export const defaultDarkTheme: Theme = {
62
+ colors: {
63
+ background: Color3.fromRGB(18, 21, 26),
64
+ surface: Color3.fromRGB(32, 37, 46),
65
+ surfaceElevated: Color3.fromRGB(40, 47, 60),
66
+ border: Color3.fromRGB(72, 80, 98),
67
+ textPrimary: Color3.fromRGB(233, 239, 246),
68
+ textSecondary: Color3.fromRGB(176, 186, 201),
69
+ accent: Color3.fromRGB(43, 105, 196),
70
+ accentContrast: Color3.fromRGB(240, 244, 250),
71
+ danger: Color3.fromRGB(129, 57, 63),
72
+ dangerContrast: Color3.fromRGB(245, 223, 226),
73
+ overlay: Color3.fromRGB(8, 10, 14),
74
+ },
75
+ space: defaultSpace,
76
+ radius: defaultRadius,
77
+ typography: defaultTypography,
78
+ };
79
+
80
+ function mergeTheme(baseTheme: Theme, partialTheme?: PartialTheme): Theme {
81
+ if (!partialTheme) {
82
+ return {
83
+ colors: { ...baseTheme.colors },
84
+ space: { ...baseTheme.space },
85
+ radius: { ...baseTheme.radius },
86
+ typography: { ...baseTheme.typography },
87
+ };
88
+ }
89
+
90
+ return {
91
+ colors: {
92
+ ...baseTheme.colors,
93
+ ...(partialTheme.colors ?? {}),
94
+ },
95
+ space: {
96
+ ...baseTheme.space,
97
+ ...(partialTheme.space ?? {}),
98
+ },
99
+ radius: {
100
+ ...baseTheme.radius,
101
+ ...(partialTheme.radius ?? {}),
102
+ },
103
+ typography: {
104
+ ...baseTheme.typography,
105
+ ...(partialTheme.typography ?? {}),
106
+ },
107
+ };
108
+ }
109
+
110
+ export function createTheme(partialTheme?: PartialTheme) {
111
+ return mergeTheme(defaultLightTheme, partialTheme);
112
+ }
@@ -0,0 +1,76 @@
1
+ import type React from "@rbxts/react";
2
+
3
+ export type ThemeColors = {
4
+ background: Color3;
5
+ surface: Color3;
6
+ surfaceElevated: Color3;
7
+ border: Color3;
8
+ textPrimary: Color3;
9
+ textSecondary: Color3;
10
+ accent: Color3;
11
+ accentContrast: Color3;
12
+ danger: Color3;
13
+ dangerContrast: Color3;
14
+ overlay: Color3;
15
+ };
16
+
17
+ export type ThemeSpace = {
18
+ 0: number;
19
+ 2: number;
20
+ 4: number;
21
+ 6: number;
22
+ 8: number;
23
+ 10: number;
24
+ 12: number;
25
+ 14: number;
26
+ 16: number;
27
+ 20: number;
28
+ 24: number;
29
+ 32: number;
30
+ };
31
+
32
+ export type ThemeRadius = {
33
+ none: number;
34
+ sm: number;
35
+ md: number;
36
+ lg: number;
37
+ xl: number;
38
+ full: number;
39
+ };
40
+
41
+ export type ThemeTypographyStyle = {
42
+ font: Enum.Font;
43
+ textSize: number;
44
+ };
45
+
46
+ export type ThemeTypography = {
47
+ labelSm: ThemeTypographyStyle;
48
+ bodyMd: ThemeTypographyStyle;
49
+ titleMd: ThemeTypographyStyle;
50
+ };
51
+
52
+ export type Theme = {
53
+ colors: ThemeColors;
54
+ space: ThemeSpace;
55
+ radius: ThemeRadius;
56
+ typography: ThemeTypography;
57
+ };
58
+
59
+ export type PartialTheme = {
60
+ colors?: Partial<ThemeColors>;
61
+ space?: Partial<ThemeSpace>;
62
+ radius?: Partial<ThemeRadius>;
63
+ typography?: Partial<ThemeTypography>;
64
+ };
65
+
66
+ export type ThemeContextValue = {
67
+ theme: Theme;
68
+ setTheme: (nextTheme: Theme) => void;
69
+ };
70
+
71
+ export type ThemeProviderProps = {
72
+ theme?: Theme;
73
+ defaultTheme?: Theme;
74
+ onThemeChange?: (nextTheme: Theme) => void;
75
+ children?: React.ReactNode;
76
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "out",
6
+ "declaration": true,
7
+ "typeRoots": [
8
+ "./node_modules/@rbxts",
9
+ "../../node_modules/@rbxts",
10
+ "./node_modules/@lattice-ui",
11
+ "../../node_modules/@lattice-ui"
12
+ ],
13
+ "types": ["types", "compiler-types"]
14
+ },
15
+ "include": ["src"]
16
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "baseUrl": "..",
6
+ "rootDir": "..",
7
+ "paths": {
8
+ "@lattice-ui/checkbox": ["checkbox/src/index.ts"],
9
+ "@lattice-ui/core": ["core/src/index.ts"],
10
+ "@lattice-ui/dialog": ["dialog/src/index.ts"],
11
+ "@lattice-ui/focus": ["focus/src/index.ts"],
12
+ "@lattice-ui/layer": ["layer/src/index.ts"],
13
+ "@lattice-ui/menu": ["menu/src/index.ts"],
14
+ "@lattice-ui/popover": ["popover/src/index.ts"],
15
+ "@lattice-ui/popper": ["popper/src/index.ts"],
16
+ "@lattice-ui/radio-group": ["radio-group/src/index.ts"],
17
+ "@lattice-ui/style": ["style/src/index.ts"],
18
+ "@lattice-ui/switch": ["switch/src/index.ts"],
19
+ "@lattice-ui/system": ["system/src/index.ts"],
20
+ "@lattice-ui/tabs": ["tabs/src/index.ts"],
21
+ "@lattice-ui/toggle-group": ["toggle-group/src/index.ts"],
22
+ "@lattice-ui/tooltip": ["tooltip/src/index.ts"]
23
+ }
24
+ }
25
+ }