diff --git a/docs/next-steps.md b/docs/next-steps.md new file mode 100644 index 0000000..cdb8b53 --- /dev/null +++ b/docs/next-steps.md @@ -0,0 +1,23 @@ +# Next Steps + +Focused follow-up work for `@knighted/develop`. + +1. **Grid-first header/layout cleanup** + - Refactor panel header layout to use CSS Grid as the primary layout mechanism. + - Reduce wrapper rows where possible and place controls explicitly in grid areas. + - Preserve existing semantics and accessibility behavior while simplifying structure. + - Validate desktop/mobile breakpoints and keep visual behavior parity. + +2. **Style isolation behavior docs** + - Document ShadowRoot on/off behavior and how style isolation changes in light DOM mode. + - Clarify that light DOM preview can inherit shell styles and include recommendations for scoping. + +3. **Preview UX polish** + - Keep tooltip affordances for mode-specific behavior. + - Continue tightening panel control alignment and spacing without introducing extra markup. + +4. **Theming (light + dark)** + - Keep the existing dark mode as the baseline and add a first-class light theme. + - Move key colors to semantic CSS variables and define both theme palettes. + - Ensure component panels, controls, editor chrome, preview shell, and tooltips all have complete light-mode coverage. + - Verify contrast/accessibility across both themes and preserve visual hierarchy parity. diff --git a/package.json b/package.json index bc509c5..6924c0a 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,17 @@ { "name": "@knighted/develop", "version": "0.1.0", - "description": "Develop UI components directly in the browser.", + "description": "Develop UI components directly in the browser using JSX and CSS.", "keywords": [ "ui", "components", "realtime", "browser", - "development" + "development", + "jsx", + "css", + "importmap", + "cdn" ], "license": "MIT", "author": "KCM ", diff --git a/src/app.js b/src/app.js index 989877b..92650bb 100644 --- a/src/app.js +++ b/src/app.js @@ -1,71 +1,27 @@ import { cdnImports, importFromCdnWithFallback } from './cdn.js' import { createCodeMirrorEditor } from './editor-codemirror.js' +import { defaultCss, defaultJsx } from './defaults.js' const statusNode = document.getElementById('status') const renderMode = document.getElementById('render-mode') const autoRenderToggle = document.getElementById('auto-render') const renderButton = document.getElementById('render-button') +const copyComponentButton = document.getElementById('copy-component') +const clearComponentButton = document.getElementById('clear-component') const styleMode = document.getElementById('style-mode') +const copyStylesButton = document.getElementById('copy-styles') +const clearStylesButton = document.getElementById('clear-styles') const shadowToggle = document.getElementById('shadow-toggle') const jsxEditor = document.getElementById('jsx-editor') const cssEditor = document.getElementById('css-editor') -const previewHost = document.getElementById('preview-host') const styleWarning = document.getElementById('style-warning') const cdnLoading = document.getElementById('cdn-loading') - -const defaultJsx = [ - 'const Button = ({ onClick }) => {', - ' return ', - '}', - '', - 'const App = () => {', - ' const onClick = () => {', - " alert('clicked!')", - ' }', - '', - ' return +
+
+

Component

+
+
+
+ + +
+
+
+
+ + + +
-
-

Styles

-
- +
+
+

Styles

+
+
+
+ + +
+
+
+
+ +
@@ -68,10 +144,22 @@

Styles

Preview

+ +
diff --git a/src/styles.css b/src/styles.css index 75fc4c8..0170403 100644 --- a/src/styles.css +++ b/src/styles.css @@ -14,6 +14,18 @@ box-sizing: border-box; } +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + body { margin: 0; padding: 0; @@ -86,6 +98,7 @@ body { .preview-panel { grid-area: preview; + position: relative; } @media (max-width: 900px) { @@ -119,6 +132,8 @@ body { padding: 16px 18px; border-bottom: 1px solid rgba(255, 255, 255, 0.08); background: rgba(15, 17, 23, 0.9); + position: relative; + z-index: 2; } .panel-header h2 { @@ -126,6 +141,26 @@ body { font-size: 1rem; } +.panel-header--stack { + align-items: stretch; + gap: 10px; +} + +.panel-header-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.panel-header-row--actions { + justify-content: flex-start; +} + +.panel-header-row--quick-actions { + justify-content: flex-start; +} + .controls { display: flex; gap: 12px; @@ -135,6 +170,14 @@ body { flex-wrap: wrap; } +.controls--actions { + justify-content: flex-start; +} + +.controls--quick-actions { + gap: 10px; +} + .controls label { display: flex; flex-direction: column; @@ -224,6 +267,8 @@ textarea:focus { padding: 18px; overflow: auto; position: relative; + background: #12141c; + z-index: 1; } .preview-host[data-style-compiling='true']::before { @@ -262,17 +307,121 @@ textarea:focus { } } -.toggle { +.controls label.toggle { flex-direction: row; align-items: center; gap: 8px; cursor: pointer; } +.controls label.color-control { + flex-direction: row; + align-items: center; + gap: 8px; +} + +.controls input[type='color'] { + width: 34px; + height: 24px; + padding: 0; + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: 8px; + background: transparent; + cursor: pointer; +} + +.controls input[type='color']::-webkit-color-swatch-wrapper { + padding: 0; +} + +.controls input[type='color']::-webkit-color-swatch { + border: none; + border-radius: 6px; +} + .toggle input { accent-color: #7a6bff; } +.hint-icon { + display: inline-grid; + place-content: center; + width: 16px; + height: 16px; + border-radius: 999px; + border: 1px solid rgba(255, 255, 255, 0.34); + color: #dbe5ff; + font-size: 0.68rem; + font-weight: 700; + line-height: 1; + opacity: 0.9; + background: transparent; +} + +.shadow-hint { + position: relative; + cursor: help; + border-width: 1px; + padding: 0; +} + +.shadow-hint::before, +.shadow-hint::after { + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: + opacity 120ms ease, + transform 120ms ease, + visibility 120ms ease; +} + +.shadow-hint::before { + content: ''; + position: absolute; + top: calc(100% + 4px); + right: 4px; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid rgba(9, 12, 20, 0.96); + transform: translateY(-4px); + z-index: 31; +} + +.shadow-hint::after { + content: attr(data-tooltip); + position: absolute; + top: calc(100% + 10px); + right: 0; + width: min(320px, calc(100vw - 36px)); + padding: 10px 12px; + border-radius: 10px; + border: 1px solid rgba(255, 255, 255, 0.12); + background: rgba(9, 12, 20, 0.96); + color: #dfe6f7; + font-size: 0.78rem; + font-weight: 500; + line-height: 1.35; + text-align: left; + box-shadow: 0 12px 24px rgba(0, 0, 0, 0.45); + transform: translateY(-4px); + z-index: 30; +} + +.shadow-hint:hover::before, +.shadow-hint:hover::after, +.shadow-hint:focus-visible::before, +.shadow-hint:focus-visible::after { + opacity: 1; + visibility: visible; + transform: translateY(0); +} + +.shadow-hint:focus-visible { + outline: 2px solid rgba(122, 107, 255, 0.9); + outline-offset: 2px; +} + .render-button { border: 1px solid rgba(255, 255, 255, 0.1); background: rgba(122, 107, 255, 0.2); @@ -287,6 +436,50 @@ textarea:focus { background: rgba(122, 107, 255, 0.35); } +.icon-button { + border: 1px solid rgba(255, 255, 255, 0.15); + background: rgba(255, 255, 255, 0.06); + color: #d8e0ef; + width: 32px; + height: 32px; + padding: 0; + border-radius: 999px; + cursor: pointer; + display: inline-grid; + place-content: center; +} + +.icon-button:hover { + background: rgba(255, 255, 255, 0.13); +} + +.icon-button:focus-visible { + outline: 2px solid rgba(122, 107, 255, 0.8); + outline-offset: 1px; +} + +.icon-button svg { + width: 16px; + height: 16px; + stroke: currentColor; + fill: none; + stroke-width: 1.8; + stroke-linecap: round; + stroke-linejoin: round; +} + +@media (max-width: 900px) { + .panel-header-row { + align-items: flex-start; + flex-direction: column; + } + + .panel-header-row--actions, + .controls--actions { + justify-content: flex-start; + } +} + .app-footer { padding: 12px 24px 24px; color: #9aa3b2;