jaml-ui 0.18.0 → 0.19.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/DESIGN.md
CHANGED
|
@@ -155,7 +155,9 @@ Contrast is critical. NEVER make grey text on top of a grey background. If using
|
|
|
155
155
|
|
|
156
156
|
## Layout
|
|
157
157
|
|
|
158
|
-
Target: Minimum 375px portrait width. Components must scale gracefully using relative units and flexible layouts. Avoid fixed widths that break at 375px.
|
|
158
|
+
Target: Minimum 375px portrait width. Components must scale gracefully using relative units and flexible layouts. Avoid fixed widths that break at 375px.
|
|
159
|
+
|
|
160
|
+
Vertical snap-scroll for ante pages using magnetic scroll-snapping (`scroll-snap-type: y mandatory`, `scroll-snap-align: start`). Horizontal swipe for seed navigation. NO visible scrollbars globally — use `::-webkit-scrollbar { display: none; }` and `-ms-overflow-style: none`.
|
|
159
161
|
|
|
160
162
|
Panels use 2px solid borders with border-silver on top/sides and border-south on bottom, creating a subtle 3D card effect. Inner shadow: `inset 0 0 0 1px rgba(255,255,255,0.04)`. Outer shadow: `0 2px 0 #000`.
|
|
161
163
|
|
|
@@ -183,6 +185,10 @@ JAML-hit items get a GlowRing: `box-shadow: 0 0 0 2px [color], 0 0 10px [color]`
|
|
|
183
185
|
|
|
184
186
|
**GlowRing:** Pulsing outline around JAML-hit items. `0 0 0 2px [color], 0 0 10px [color]`. Animation: opacity 0.55 → 1.0 over 1.6s ease-in-out infinite.
|
|
185
187
|
|
|
188
|
+
**Card Hover Juice:** Cards receive a 3D magnetic tilt plus a "juice-up" scale animation (`scale(1.05) translateY(-2px)`) on hover to mimic a physical interaction.
|
|
189
|
+
|
|
190
|
+
**Font Dance:** Text can be made to wiggle with a staggered `translateY` offset animation across characters (`.j-font-dance-char`), creating a cozy pixel font animation.
|
|
191
|
+
|
|
186
192
|
**SeedPagerHeader:** Three columns: [left stride arrow] [identity panel with seed + copy + deck/stake] [right stride arrow]. Stride arrows are tall red bars with dark-red inset shadow. Identity panel is translucent with counter pip.
|
|
187
193
|
|
|
188
194
|
## Do's and Don'ts
|
|
@@ -194,6 +200,7 @@ JAML-hit items get a GlowRing: `box-shadow: 0 0 0 2px [color], 0 0 10px [color]`
|
|
|
194
200
|
- DON'T use ALL CAPS. It is considered an embellishment and ruins the aesthetic.
|
|
195
201
|
- DON'T put grey text on top of a grey background.
|
|
196
202
|
- DON'T use fat padding or margins. Balatro UI is dense and cozy.
|
|
197
|
-
- DON'T add
|
|
203
|
+
- DON'T add visible scrollbars. Vertical magnetic snap-scroll + horizontal swipe only.
|
|
198
204
|
- DON'T use rounded corners larger than 10px. Balatro is chunky, not bubbly.
|
|
199
205
|
- DON'T use blur-based shadows for depth. Use solid colored box-shadows 80% opaque.
|
|
206
|
+
- DON'T use redundant JS wrappers for `motely-wasm`. Import globally and `motely.boot()` once.
|
|
@@ -113,7 +113,7 @@ export function JamlMapEditor({ zone: initialZone = "must", onChange, }) {
|
|
|
113
113
|
flexDirection: "column",
|
|
114
114
|
gap: 24,
|
|
115
115
|
borderBottom: `2px solid ${C.DARK_GREY}`
|
|
116
|
-
}, children: [_jsxs(JimboText, { size: "md", tone: "
|
|
116
|
+
}, children: [_jsxs(JimboText, { size: "md", tone: "white", dance: true, style: { textAlign: "center", marginBottom: 8 }, children: ["ANTE ", a] }), _jsxs("div", { className: "j-flex j-justify-between j-items-end", children: [_jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "VOUCHER" }), renderSlot(a, `ante_${a}_voucher`, 42, "Vouchers", "voucher")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "SMALL" }), renderSlot(a, `ante_${a}_tag_small`, 42, "tags", "tag")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "BIG" }), renderSlot(a, `ante_${a}_tag_big`, 42, "tags", "tag")] }), _jsxs("div", { className: "j-flex-col j-items-center j-gap-xs", children: [_jsx(JimboText, { size: "micro", tone: "grey", children: "BOSS" }), renderSlot(a, `ante_${a}_boss`, 42, "BlindChips", "boss")] })] }), _jsxs("div", { className: "j-flex-col j-gap-xs", children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { letterSpacing: 1 }, children: "SHOP ITEMS" }), _jsx("div", { className: "j-flex hide-scrollbar j-gap-sm", style: { overflowX: "auto", paddingBottom: 8 }, children: [1, 2, 3, 4, 5, 6, 7, 8].map(i => renderSlot(a, `ante_${a}_shop_${i}`, 52, "Jokers")) })] }), _jsxs("div", { className: "j-flex-col j-gap-xs", children: [_jsx(JimboText, { size: "xs", tone: "grey", style: { letterSpacing: 1 }, children: "PACKS" }), _jsx("div", { className: "j-flex j-gap-sm", style: { flexWrap: "wrap" }, children: [1, 2, 3, 4, 5, 6].map(i => renderSlot(a, `ante_${a}_pack_${i}`, 64, "Boosters", "pack")) })] })] }, a))) }), _jsx(JimboModal, { open: activeSlot !== null, onClose: handlePickerCancel, title: pickerFlow === "category" ? "Select Category" : undefined, className: "j-picker-modal", children: activeSlot !== null && (pickerFlow === "category" ? (_jsx(CategoryMenu, { onSelect: handleCategorySelect })) : pickerFlow === "joker" ? (_jsx(JokerPicker, { onSelect: handleItemSelect, onCancel: handlePickerCancel })) : (_jsx(CategoryPicker, { config: CATEGORY_CONFIG_MAP[pickerFlow], onSelect: handleItemSelect, onCancel: handlePickerCancel }))) })] }));
|
|
117
117
|
}
|
|
118
118
|
// ─── Category Selection Menu ─────────────────────────────────────────────────
|
|
119
119
|
function CategoryMenu({ onSelect, }) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useState, useCallback } from "react";
|
|
3
|
-
import { MotelyWasm, Motely } from "motely-wasm";
|
|
3
|
+
import motely, { MotelyWasm, Motely } from "motely-wasm";
|
|
4
|
+
// Boot motely immediately when this module is loaded
|
|
5
|
+
motely.boot().catch(console.error);
|
|
4
6
|
import { extractVisualJamlItems } from "../utils/jamlMapPreview.js";
|
|
5
7
|
import { motelyItemDisplayNameFromValue } from "../motelyDisplay.js";
|
|
6
8
|
export function useAnalyzer() {
|
package/dist/ui/hooks.js
CHANGED
|
@@ -397,7 +397,9 @@ export function useJamlCardRenderer({ layers, invert = false, hoverTilt = false,
|
|
|
397
397
|
const y = event.clientY - rect.top;
|
|
398
398
|
const rotateY = (x / rect.width) * 12 - 6;
|
|
399
399
|
const rotateX = (y / rect.height) * -16 + 8;
|
|
400
|
-
|
|
400
|
+
const juiceScale = 1.05;
|
|
401
|
+
const juiceY = -2; // slight move up
|
|
402
|
+
setTransform(`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(${juiceScale}) translateY(${juiceY}px)`);
|
|
401
403
|
};
|
|
402
404
|
const containerStyle = {
|
|
403
405
|
aspectRatio: String(ratio),
|
package/dist/ui/jimbo.css
CHANGED
|
@@ -1047,3 +1047,22 @@
|
|
|
1047
1047
|
background: var(--j-darkest);
|
|
1048
1048
|
border: 1px solid var(--j-panel-edge);
|
|
1049
1049
|
}
|
|
1050
|
+
|
|
1051
|
+
/* ── Font Dance Animation ─────────────────────────────────────────────── */
|
|
1052
|
+
|
|
1053
|
+
@keyframes j-font-dance {
|
|
1054
|
+
0% { transform: translateY(0); }
|
|
1055
|
+
25% { transform: translateY(-1px); }
|
|
1056
|
+
50% { transform: translateY(0); }
|
|
1057
|
+
75% { transform: translateY(1px); }
|
|
1058
|
+
100% { transform: translateY(0); }
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
.j-text--dance-container {
|
|
1062
|
+
display: inline-flex;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
.j-font-dance-char {
|
|
1066
|
+
display: inline-block;
|
|
1067
|
+
animation: j-font-dance 3s infinite ease-in-out;
|
|
1068
|
+
}
|
package/dist/ui/jimboText.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
export type JimboTextTone = 'default' | 'mult' | 'chips' | 'gold' | 'green' | 'red' | 'blue' | 'orange' | 'purple' | 'grey';
|
|
2
|
+
export type JimboTextTone = 'default' | 'mult' | 'chips' | 'gold' | 'green' | 'red' | 'blue' | 'orange' | 'purple' | 'grey' | 'white';
|
|
3
3
|
export type JimboTextSize = 'micro' | 'label' | 'xs' | 'body' | 'sm' | 'md' | 'heading' | 'lg' | 'xl' | 'display';
|
|
4
4
|
export interface JimboTextProps extends React.HTMLAttributes<HTMLElement> {
|
|
5
5
|
tone?: JimboTextTone;
|
|
@@ -8,6 +8,8 @@ export interface JimboTextProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
8
8
|
shadow?: boolean;
|
|
9
9
|
/** Uppercase + spacing — Balatro button/pill label treatment. Default false. */
|
|
10
10
|
uppercase?: boolean;
|
|
11
|
+
/** Wiggle effect for text characters. Default false. */
|
|
12
|
+
dance?: boolean;
|
|
11
13
|
/** Letter-spacing override; defaults depend on uppercase prop. */
|
|
12
14
|
letterSpacing?: number | string;
|
|
13
15
|
as?: 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label';
|
|
@@ -23,4 +25,4 @@ export interface JimboTextProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
23
25
|
* so the @font-face declaration lands (font is base64-embedded, no
|
|
24
26
|
* runtime fetch).
|
|
25
27
|
*/
|
|
26
|
-
export declare function JimboText({ tone, size, shadow, uppercase, letterSpacing, as: Tag, className, style, children, ...rest }: JimboTextProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export declare function JimboText({ tone, size, shadow, uppercase, dance, letterSpacing, as: Tag, className, style, children, ...rest }: JimboTextProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/ui/jimboText.js
CHANGED
|
@@ -10,11 +10,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
10
10
|
* so the @font-face declaration lands (font is base64-embedded, no
|
|
11
11
|
* runtime fetch).
|
|
12
12
|
*/
|
|
13
|
-
export function JimboText({ tone = 'default', size = 'md', shadow = true, uppercase = false, letterSpacing, as: Tag = 'span', className = '', style, children, ...rest }) {
|
|
13
|
+
export function JimboText({ tone = 'default', size = 'md', shadow = true, uppercase = false, dance = false, letterSpacing, as: Tag = 'span', className = '', style, children, ...rest }) {
|
|
14
14
|
const sizeClass = `j-text--${size}`;
|
|
15
15
|
const toneClass = `j-text--${tone}`;
|
|
16
16
|
const shadowClass = shadow ? '' : 'j-text--no-shadow';
|
|
17
17
|
const upperClass = uppercase ? 'j-text--upper' : '';
|
|
18
|
+
const danceClass = dance ? 'j-text--dance-container' : '';
|
|
18
19
|
const inlineStyle = {};
|
|
19
20
|
if (letterSpacing != null) {
|
|
20
21
|
inlineStyle.letterSpacing = letterSpacing;
|
|
@@ -24,5 +25,9 @@ export function JimboText({ tone = 'default', size = 'md', shadow = true, upperc
|
|
|
24
25
|
}
|
|
25
26
|
if (style)
|
|
26
27
|
Object.assign(inlineStyle, style);
|
|
27
|
-
|
|
28
|
+
let content = children;
|
|
29
|
+
if (dance && typeof children === 'string') {
|
|
30
|
+
content = children.split('').map((char, i) => (_jsx("span", { className: "j-font-dance-char", style: { animationDelay: `${i * -0.15}s` }, children: char === ' ' ? '\u00A0' : char }, i)));
|
|
31
|
+
}
|
|
32
|
+
return (_jsx(Tag, { className: `j-text ${sizeClass} ${toneClass} ${shadowClass} ${upperClass} ${danceClass} ${className}`.trim(), style: Object.keys(inlineStyle).length > 0 ? inlineStyle : undefined, ...rest, children: content }));
|
|
28
33
|
}
|