diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b190d05..136b32e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -76,4 +76,4 @@ jobs: - name: Check linting working-directory: ./typescript - run: npm run lint:check + run: npm run lint diff --git a/typescript/README.md b/typescript/README.md index 147e2b4..f5500c1 100644 --- a/typescript/README.md +++ b/typescript/README.md @@ -244,20 +244,23 @@ MIT - [x] setup eslint - [x] improve tsconfig -- [ ] setup lint staged and husky -- [ ] setup prettier -- [ ] standardize styling (css) -- [ ] fix styling for elements - - [ ] headings - - [ ] paragraph - - [ ] code - - [ ] table +- [x] setup lint staged and husky +- [x] setup prettier +- [x] standardize styling (css) +- [x] fix styling for elements + - [x] headings + - [x] paragraph + - [x] code + - [x] table - [ ] todo? - - [ ] equation + - [x] equation - [ ] image - - [ ] blockquote -- fix ts and eslint errors in these dirs and remove from ignore list of ts and eslint + - [x] blockquote +- [] fix ts and eslint errors in these dirs and remove from ignore list of ts and eslint ``` "**/generated/**", "src/serialization/**", ``` +- [] setup up bumpp: https://www.npmjs.com/package/bumpp +- [] migrate test runner to vitest +- [] move vite app to typescript root examples dir diff --git a/typescript/eslint.config.js b/typescript/eslint.config.js index b3b5bb7..a313898 100644 --- a/typescript/eslint.config.js +++ b/typescript/eslint.config.js @@ -41,6 +41,7 @@ module.exports = [ import: importPlugin, }, rules: { + "no-var": ["warn"], // TypeScript rules "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-module-boundary-types": "off", diff --git a/typescript/package.json b/typescript/package.json index d7b317b..362a073 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -20,7 +20,6 @@ "check": "tsc --noEmit", "lint": "eslint src --ext .ts,.tsx", "lint:fix": "eslint src --ext .ts,.tsx --fix", - "lint:check": "eslint src --ext .ts,.tsx --max-warnings 0", "prepare": "husky" }, "keywords": [ diff --git a/typescript/src/examples/index.tsx b/typescript/src/examples/index.tsx index dc51f3f..8877f65 100644 --- a/typescript/src/examples/index.tsx +++ b/typescript/src/examples/index.tsx @@ -6,32 +6,22 @@ import { HeadingBlockRenderer } from "@/renderer/components/blocks/HeadingBlockR import { JsonDocRenderer } from "../renderer/JsonDocRenderer"; -// import testPage from "./testJsonDocs/test_document.json"; import testPage from "./testJsonDocs/ex1_success.json"; -// import testPage from "./testJsonDocs/test_document_2.json"; const App = () => { - // async function main() { - // const schema = await loadSchema("./testJsonDocs/test_document_2.json"); - - // try { - // const isValid = validateAgainstSchema(testPage, schema); - // console.log("isvlaid: ", isValid); - // console.log("schema: ", schema); - // } catch (error) { - // console.log("error validating schema: ", error); - // } - // } - - // useEffect(() => { - // main(); - // }, []); - return ( -
+

JSON-DOC Renderer Development

{ return ; diff --git a/typescript/src/renderer/JsonDocRenderer.tsx b/typescript/src/renderer/JsonDocRenderer.tsx index 9193f04..1cc86c1 100644 --- a/typescript/src/renderer/JsonDocRenderer.tsx +++ b/typescript/src/renderer/JsonDocRenderer.tsx @@ -1,21 +1,23 @@ +import "./styles/index.css"; import React from "react"; import { BlockRenderer } from "./components/BlockRenderer"; -import "./styles.css"; interface JsonDocRendererProps { page: any; className?: string; components?: React.ComponentProps["components"]; + theme?: "light" | "dark"; } export const JsonDocRenderer = ({ page, className = "", components, + theme = "light", }: JsonDocRendererProps) => { return ( -
+
{/* Page icon */} {page.icon && ( diff --git a/typescript/src/renderer/components/blocks/CodeBlockRenderer.tsx b/typescript/src/renderer/components/blocks/CodeBlockRenderer.tsx index c0e6d50..3abcf01 100644 --- a/typescript/src/renderer/components/blocks/CodeBlockRenderer.tsx +++ b/typescript/src/renderer/components/blocks/CodeBlockRenderer.tsx @@ -26,13 +26,11 @@ export const CodeBlockRenderer: React.FC = ({ >
-
-
-
{codeData?.language || "Plain Text"}
-
+
+ {codeData?.language || "Plain Text"}
-
+
diff --git a/typescript/src/renderer/components/blocks/ParagraphBlockRenderer.tsx b/typescript/src/renderer/components/blocks/ParagraphBlockRenderer.tsx index 74ddcf7..3834ab7 100644 --- a/typescript/src/renderer/components/blocks/ParagraphBlockRenderer.tsx +++ b/typescript/src/renderer/components/blocks/ParagraphBlockRenderer.tsx @@ -17,20 +17,14 @@ export const ParagraphBlockRenderer: React.FC = ({ components, ...props }) => { - console.log("block.paragraph: ", block.paragraph); - console.log("block children:", block.children); return (
-
-
-
- -
-
+
+
{/* Render children blocks recursively */} diff --git a/typescript/src/renderer/index.ts b/typescript/src/renderer/index.ts index 2e96547..5cb2d09 100644 --- a/typescript/src/renderer/index.ts +++ b/typescript/src/renderer/index.ts @@ -1,13 +1,14 @@ -export { JsonDocRenderer } from "./JsonDocRenderer"; export { BlockRenderer } from "./components/BlockRenderer"; // Export individual block components for composition export { ParagraphBlockRenderer } from "./components/blocks/ParagraphBlockRenderer"; export { HeadingBlockRenderer } from "./components/blocks/HeadingBlockRenderer"; export { ListItemBlockRenderer } from "./components/blocks/ListItemBlockRenderer"; + export { CodeBlockRenderer } from "./components/blocks/CodeBlockRenderer"; export { ImageBlockRenderer } from "./components/blocks/ImageBlockRenderer"; export { TableBlockRenderer } from "./components/blocks/TableBlockRenderer"; +export { JsonDocRenderer } from "./JsonDocRenderer"; export { QuoteBlockRenderer } from "./components/blocks/QuoteBlockRenderer"; export { DividerBlockRenderer } from "./components/blocks/DividerBlockRenderer"; export { ToDoBlockRenderer } from "./components/blocks/ToDoBlockRenderer"; @@ -20,3 +21,6 @@ export { OrderedListBlockRenderer } from "./components/blocks/OrderedListBlockRe // Export types export type { BlockComponents } from "./components/BlockRenderer"; export type { JsonDocRendererProps, BlockRendererProps } from "./types"; + +// Import default styles - users can override by importing their own after this +import "./styles/index.css"; diff --git a/typescript/src/renderer/styles.css b/typescript/src/renderer/styles.css deleted file mode 100644 index cb09ef5..0000000 --- a/typescript/src/renderer/styles.css +++ /dev/null @@ -1,417 +0,0 @@ -.json-doc-page { - max-width: 100%; - margin: 0 auto; - padding: 96px 96px 30vh; -} - -.json-doc-page-icon { - font-size: 78px; - line-height: 1.1; - margin-bottom: 0.1em; -} - -.json-doc-page-title { - font-size: 40px; - line-height: 1.2; - font-weight: 700; - margin: 0 0 2px; - padding: 3px 2px; -} - -.json-doc-page-content { - margin-top: 16px; -} - -/* Block Styles */ -.notion-selectable { - position: relative; - margin: 0; - padding: 2px 2px; -} - -.notion-block-children { - margin-top: 0; -} - -/* Improve spacing between blocks */ -.notion-selectable + .notion-selectable { - margin-top: 0; -} - -/* Better spacing for headings */ -.notion-header-block, -.notion-sub_header-block { - margin: 16px 0 4px 0; -} - -.notion-header-block:first-child, -.notion-sub_header-block:first-child { - margin-top: 0; -} - -.notranslate { - min-height: 1em; - white-space: pre-wrap; - word-break: break-word; -} - -/* Text Block */ -.notion-text-block { - padding: 2px 2px; -} - -/* Heading Blocks */ -.notion-header-block h2 { - font-size: 1.875em; - margin: 0; - font-weight: 600; - line-height: 1.3; - padding: 3px 2px; -} - -.notion-sub_header-block h3 { - font-size: 1.5em; - margin: 0; - font-weight: 600; - line-height: 1.3; - padding: 3px 2px; -} - -.notion-sub_header-block h4 { - font-size: 1.25em; - margin: 0; - font-weight: 600; - line-height: 1.3; - padding: 3px 2px; -} - -/* List Container Blocks */ -.notion-unordered_list-block, -.notion-ordered_list-block { - margin: 2px 0; - padding-left: 0; -} - -.notion-unordered_list-block { - list-style-type: disc; - padding-left: 1.5em; -} - -.notion-ordered_list-block { - list-style-type: decimal; - padding-left: 1.5em; -} - -/* List Items - Match Notion spacing exactly */ -.notion-bulleted_list-block, -.notion-numbered_list-block { - margin: 0; - padding: 1px 0; - line-height: 1.5; -} - -.notion-checkbox { - width: 14px; - height: 14px; - margin: 0; - cursor: pointer; -} - -/* Code Block */ -.notion-code-block { - background: rgb(247, 246, 243); - border-radius: 3px; - padding: 16px; - margin: 4px 0; -} - -.notion-code-block .line-numbers { - font-family: - "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; - font-size: 14px; - line-height: 1.4; - white-space: pre; - overflow-x: auto; -} - -/* Quote Block */ -.notion-quote-block { - padding: 3px 2px; -} - -.notion-quote-block blockquote { - margin: 0; - padding-left: 14px; - border-left: 3px solid currentColor; - font-size: 1em; - line-height: 1.5; -} - -/* Divider Block */ -.notion-divider-block { - padding: 6px 2px; -} - -.notion-divider-block [role="separator"] { - border-top: 1px solid rgba(55, 53, 47, 0.16); - margin: 0; -} - -/* To-do Block */ -.notion-to_do-block { - display: flex; - align-items: flex-start; - padding: 1px 2px; - margin: 0; -} - -.notion-to_do-block .checkboxSquare, -.notion-to_do-block .check { - width: 16px; - height: 16px; - cursor: pointer; -} - -.notion-to_do-block .check { - color: #0f7b0f; -} - -.pseudoHover.pseudoActive { - position: relative; - display: flex; - align-items: center; - justify-content: center; -} - -/* Toggle Block */ -.notion-toggle-block { - display: flex; - align-items: flex-start; - padding: 3px 2px; -} - -.notion-toggle-block .arrowCaretDownFillSmall { - width: 16px; - height: 16px; - color: rgba(55, 53, 47, 0.45); -} - -/* Table Block */ -.notion-table-block { - margin: 4px 0; - width: 100%; -} - -.notion-table { - width: 100%; - border-collapse: collapse; - border-spacing: 0; - table-layout: fixed; -} - -.notion-table-row th, -.notion-table-row td { - border: 1px solid rgb(233, 233, 231); - padding: 6px 8px; - vertical-align: top; - word-wrap: break-word; -} - -/* Image Block */ -.notion-image-block { - padding: 3px 2px; -} - -.notion-image-placeholder { - width: 300px; - height: 200px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 8px; - position: relative; - overflow: hidden; - margin: 10px 0; -} - -.notion-image-placeholder::before { - content: ""; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 60%; - background: linear-gradient(to top, #2c3e50 0%, #3498db 70%); - clip-path: polygon(0 100%, 30% 60%, 60% 80%, 100% 50%, 100% 100%); -} - -.notion-image-placeholder::after { - content: ""; - position: absolute; - top: 20px; - right: 30px; - width: 40px; - height: 40px; - background: #f1c40f; - border-radius: 50%; - box-shadow: 0 0 20px rgba(241, 196, 15, 0.3); -} - -.notion-image-caption { - color: #37352f; - font-size: 14px; - margin-top: 8px; -} - -/* Column Layout */ -.notion-column-list { - display: flex; - gap: 16px; - width: 100%; -} - -.notion-column { - flex: 1; - min-width: 0; -} - -/* Toggle Block */ -.notion-toggle-content { - display: flex; - align-items: center; - gap: 8px; -} - -.notion-toggle-arrow { - color: rgba(55, 53, 47, 0.45); - font-size: 12px; - transition: transform 0.2s ease; -} - -.notion-toggle-text { - flex: 1; -} - -/* Column Layout */ -.notion-column_list-block { - margin: 4px 0; -} - -.notion-column-list { - display: flex; - gap: 16px; -} - -.notion-column { - flex: 1; - min-width: 0; -} - -/* Equation Block */ -.notion-equation-block { - padding: 3px 2px; - margin: 4px 0; -} - -.notion-equation-display { - text-align: center; - padding: 16px; - background: rgb(247, 246, 243); - border-radius: 3px; -} - -.notion-equation-content { - font-family: "Times New Roman", serif; - font-size: 1.2em; -} - -/* Rich Text Formatting */ -.notion-inline-code { - background: rgba(135, 131, 120, 0.15); - color: #eb5757; - border-radius: 3px; - padding: 0.2em 0.4em; - font-family: - "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; - font-size: 85%; -} - -.notion-link { - color: inherit; - word-wrap: break-word; - cursor: pointer; - text-decoration: underline; - text-decoration-color: rgba(55, 53, 47, 0.4); -} - -.notion-link:hover { - text-decoration-color: rgba(55, 53, 47, 1); -} - -.notion-equation { - background: rgba(135, 131, 120, 0.15); - border-radius: 3px; - padding: 0.2em 0.4em; - font-family: "Times New Roman", serif; -} - -/* Text Colors */ -.notion-text-color-gray { - color: rgba(120, 119, 116, 1); -} - -.notion-text-color-brown { - color: rgba(159, 107, 83, 1); -} - -.notion-text-color-orange { - color: rgba(217, 115, 13, 1); -} - -.notion-text-color-yellow { - color: rgba(203, 145, 47, 1); -} - -.notion-text-color-green { - color: rgba(68, 131, 97, 1); -} - -.notion-text-color-blue { - color: rgba(51, 126, 169, 1); -} - -.notion-text-color-purple { - color: rgba(144, 101, 176, 1); -} - -.notion-text-color-pink { - color: rgba(193, 76, 138, 1); -} - -.notion-text-color-red { - color: rgba(212, 76, 71, 1); -} - -/* Unsupported Block */ -.notion-unsupported-block { - padding: 8px; - background: rgba(255, 0, 0, 0.1); - border: 1px solid rgba(255, 0, 0, 0.3); - border-radius: 3px; - color: #d32f2f; - font-style: italic; -} - -/* Responsive Design */ -@media (max-width: 768px) { - .json-doc-page { - padding: 48px 24px 30vh; - } - - .json-doc-page-title { - font-size: 32px; - } - - .notion-column-list { - flex-direction: column; - gap: 8px; - } -} diff --git a/typescript/src/renderer/styles/base.css b/typescript/src/renderer/styles/base.css new file mode 100644 index 0000000..5950d1e --- /dev/null +++ b/typescript/src/renderer/styles/base.css @@ -0,0 +1,49 @@ +/* Base Layout and Page Styles */ + +.json-doc-page { + max-width: var(--jsondoc-page-max-width); + margin: 0 auto; + padding: var(--jsondoc-page-padding-desktop) + var(--jsondoc-page-padding-desktop) var(--jsondoc-page-bottom-padding); + color: var(--jsondoc-text-primary); + font-family: var(--jsondoc-font-family-sans); +} + +.json-doc-page-icon { + font-size: var(--jsondoc-font-size-page-icon); + line-height: var(--jsondoc-line-height-tight); + margin-bottom: 0.1em; +} + +.json-doc-page-title { + font-size: var(--jsondoc-font-size-page-title); + line-height: var(--jsondoc-line-height-normal); + font-weight: var(--jsondoc-font-weight-bold); + margin: 0 0 var(--jsondoc-spacing-xs); + padding: var(--jsondoc-spacing-sm) 0px; +} + +.json-doc-page-content { + margin-top: var(--jsondoc-spacing-lg); +} + +/* Base Block Styles */ +.notion-selectable { + position: relative; + margin: 0; + padding: var(--jsondoc-spacing-sm) 0px; +} + +.notion-block-children { + margin-top: 0; +} + +.notion-selectable + .notion-selectable { + margin-top: 0; +} + +.notranslate { + min-height: 1em; + white-space: pre-wrap; + word-break: break-word; +} diff --git a/typescript/src/renderer/styles/blocks.css b/typescript/src/renderer/styles/blocks.css new file mode 100644 index 0000000..a90da43 --- /dev/null +++ b/typescript/src/renderer/styles/blocks.css @@ -0,0 +1,141 @@ +/* Block Component Styles */ + +/* Code Block */ +.notion-code-block { + background: var(--jsondoc-bg-code); + border-radius: var(--jsondoc-radius-sm); + padding: var(--jsondoc-spacing-lg); + margin: var(--jsondoc-spacing-sm) 0; +} + +.notion-code-block .line-numbers { + font-family: var(--jsondoc-font-family-mono); + font-size: var(--jsondoc-font-size-code); + line-height: var(--jsondoc-line-height-loose); + white-space: pre; + overflow-x: auto; +} + +.notion-code-block-content { + padding: var(--jsondoc-spacing-lg); +} + +.notion-code-block-language { + color: var(--jsondoc-text-muted); + font-size: var(--jsondoc-font-size-code); +} + +.notion-inline-code { + background: var(--jsondoc-bg-inline-code); + color: var(--jsondoc-accent-inline-code); + border-radius: var(--jsondoc-radius-sm); + padding: 0.2em 0.4em; + font-family: var(--jsondoc-font-family-mono); + font-size: var(--jsondoc-font-size-inline-code); +} + +/* Quote Block */ +.notion-quote-block { + padding: var(--jsondoc-spacing-sm) var(--jsondoc-spacing-xs); +} + +.notion-quote-block blockquote { + margin: 0; + padding-left: 14px; + border-left: 3px solid currentColor; + font-size: var(--jsondoc-font-size-body); + line-height: var(--jsondoc-line-height-very-loose); +} + +/* Divider Block */ +.notion-divider-block { + padding: 6px var(--jsondoc-spacing-xs); +} + +.notion-divider-block [role="separator"] { + border-top: 1px solid var(--jsondoc-border-light); + margin: 0; +} + +/* To-do Block */ +.notion-to_do-block { + display: flex; + align-items: flex-start; + padding: 1px var(--jsondoc-spacing-xs); + margin: 0; +} + +.notion-to_do-block .checkboxSquare, +.notion-to_do-block .check { + width: var(--jsondoc-toggle-checkbox-size); + height: var(--jsondoc-toggle-checkbox-size); + cursor: pointer; +} + +.notion-to_do-block .check { + color: var(--jsondoc-accent-checkbox); +} + +.pseudoHover.pseudoActive { + position: relative; + display: flex; + align-items: center; + justify-content: center; +} + +/* Toggle Block */ +.notion-toggle-block { + display: flex; + align-items: flex-start; + padding: var(--jsondoc-spacing-sm) var(--jsondoc-spacing-xs); +} + +.notion-toggle-block .arrowCaretDownFillSmall { + width: var(--jsondoc-icon-size); + height: var(--jsondoc-icon-size); + color: var(--jsondoc-text-muted); +} + +.notion-toggle-content { + display: flex; + align-items: center; + gap: var(--jsondoc-spacing-md); +} + +.notion-toggle-arrow { + color: var(--jsondoc-text-muted); + font-size: 12px; + transition: transform var(--jsondoc-transition-fast); +} + +.notion-toggle-text { + flex: 1; +} + +/* Equation Block */ +.notion-equation-block { + padding: var(--jsondoc-spacing-sm) var(--jsondoc-spacing-xs); + margin: var(--jsondoc-spacing-sm) 0; +} + +.notion-equation-display { + text-align: center; + padding: var(--jsondoc-spacing-lg); + background: var(--jsondoc-bg-code); + border-radius: var(--jsondoc-radius-sm); +} + +.notion-equation-content { + font-family: var(--jsondoc-font-family-serif); + font-size: var(--jsondoc-font-size-equation); +} + +/* Unsupported Block */ +.notion-unsupported-block { + padding: var(--jsondoc-spacing-md); + background: var(--jsondoc-bg-unsupported); + border: 1px solid rgba(255, 0, 0, 0.3); + border-radius: var(--jsondoc-radius-sm); + color: var(--jsondoc-accent-error); + font-style: italic; +} diff --git a/typescript/src/renderer/styles/doc.md b/typescript/src/renderer/styles/doc.md new file mode 100644 index 0000000..f8037ea --- /dev/null +++ b/typescript/src/renderer/styles/doc.md @@ -0,0 +1,259 @@ +# JSON-DOC Renderer Styles + +This directory contains the modular CSS architecture for the JSON-DOC renderer. The styles are organized into logical modules with customizable CSS variables for easy theming. + +## Architecture + +### File Structure + +``` +styles/ +├── index.css # Main entry point - imports all modules +├── variables.css # CSS custom properties (design tokens) +├── base.css # Base layout and page styles +├── typography.css # Text and heading styles +├── blocks.css # Block component styles +├── lists.css # List-specific styles +├── table.css # Table styles +├── media.css # Image and media styles +├── layout.css # Layout components (columns) +├── responsive.css # Responsive breakpoints +└── README.md # This file +``` + +## Usage + +### Default Import + +The renderer automatically imports the default styles: + +```typescript +import { JsonDocRenderer } from "@json-doc/typescript"; +// Styles are automatically included +``` + +### Custom Styling + +To customize styles, you can: + +1. **Override CSS Variables** (recommended): + +```css +:root { + --jsondoc-text-primary: #2d3748; + --jsondoc-font-family-sans: "Inter", sans-serif; + --jsondoc-spacing-lg: 20px; +} +``` + +2. **Import Individual Modules**: + +```css +@import "@json-doc/typescript/dist/renderer/styles/variables.css"; +@import "@json-doc/typescript/dist/renderer/styles/base.css"; +/* Import only what you need */ +``` + +3. **Create Your Own Theme**: + +```css +/* my-theme.css */ +@import "@json-doc/typescript/dist/renderer/styles/variables.css"; + +:root { + /* Override variables */ + --jsondoc-text-primary: #1a202c; + --jsondoc-bg-code: #f7fafc; +} + +@import "@json-doc/typescript/dist/renderer/styles/base.css"; +/* Import other modules as needed */ +``` + +## CSS Variables (Design Tokens) + +### Colors + +- `--jsondoc-text-primary`: Primary text color +- `--jsondoc-text-secondary`: Secondary text color +- `--jsondoc-text-muted`: Muted text color +- `--jsondoc-border-light`: Light border color +- `--jsondoc-border-medium`: Medium border color + +### Background Colors + +- `--jsondoc-bg-code`: Code block background +- `--jsondoc-bg-inline-code`: Inline code background +- `--jsondoc-bg-unsupported`: Unsupported block background + +### Text Colors + +- `--jsondoc-color-gray` through `--jsondoc-color-red`: Notion-style text colors + +### Typography + +- `--jsondoc-font-family-sans`: Sans-serif font stack +- `--jsondoc-font-family-mono`: Monospace font stack +- `--jsondoc-font-family-serif`: Serif font stack +- `--jsondoc-font-size-*`: Font sizes for different elements +- `--jsondoc-font-weight-*`: Font weights +- `--jsondoc-line-height-*`: Line heights + +### Spacing + +- `--jsondoc-spacing-xs` through `--jsondoc-spacing-3xl`: Consistent spacing scale + +### Layout + +- `--jsondoc-page-max-width`: Maximum page width +- `--jsondoc-page-padding-desktop`: Desktop page padding +- `--jsondoc-page-padding-mobile`: Mobile page padding +- `--jsondoc-column-gap`: Gap between columns + +## Theming + +### Light/Dark Mode + +The styles include automatic dark mode support: + +```css +@media (prefers-color-scheme: dark) { + :root { + --jsondoc-text-primary: #ffffff; + /* Other dark mode overrides */ + } +} +``` + +### Manual Theme Classes + +Force specific themes using classes: + +```html +
+ +
+ +
+ +
+``` + +### Custom Color Schemes + +Create your own color schemes: + +```css +.my-custom-theme { + --jsondoc-text-primary: #2b6cb0; + --jsondoc-bg-code: #ebf4ff; + --jsondoc-color-blue: #3182ce; +} +``` + +## Responsive Design + +The styles include responsive breakpoints: + +- Desktop: Default styles +- Tablet: `max-width: 768px` +- Mobile: `max-width: 480px` + +Override responsive behavior: + +```css +@media (max-width: 768px) { + :root { + --jsondoc-page-padding-desktop: 16px; + } +} +``` + +## Block-Specific Styling + +Each block type has its own CSS class namespace: + +- `.notion-text-block`: Paragraph blocks +- `.notion-header-block`: Heading 1 blocks +- `.notion-sub_header-block`: Heading 2/3 blocks +- `.notion-code-block`: Code blocks +- `.notion-quote-block`: Quote blocks +- `.notion-to_do-block`: To-do blocks +- `.notion-table-block`: Table blocks +- `.notion-image-block`: Image blocks +- `.notion-column_list-block`: Column list blocks + +## Advanced Customization + +### Overriding Specific Components + +```css +/* Custom code block styling */ +.notion-code-block { + background: var(--my-code-bg); + border: 1px solid var(--my-code-border); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} +``` + +### Adding Custom Animations + +```css +.notion-selectable { + transition: all 0.2s ease; +} + +.notion-selectable:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} +``` + +### Print Styles + +```css +@media print { + .json-doc-page { + padding: 0; + max-width: none; + } + + .notion-toggle-block { + /* Expand all toggles for print */ + } +} +``` + +## Browser Compatibility + +The styles use modern CSS features: + +- CSS Custom Properties (CSS Variables) +- CSS Grid and Flexbox +- CSS Logical Properties + +Supports: + +- Chrome/Edge 49+ +- Firefox 31+ +- Safari 9.1+ + +For older browser support, consider using a CSS preprocessor to compile variables to static values. + +## Performance Considerations + +- CSS is tree-shakeable when importing individual modules +- Variables are computed at runtime but cached by browsers +- No JavaScript dependencies for styling +- Minimal CSS bundle size (~15KB gzipped) + +## Contributing + +When adding new styles: + +1. Add design tokens to `variables.css` first +2. Use existing variables instead of hardcoded values +3. Follow the modular structure +4. Test responsive behavior +5. Ensure dark mode compatibility +6. Update this README with new variables or classes diff --git a/typescript/src/renderer/styles/index.css b/typescript/src/renderer/styles/index.css new file mode 100644 index 0000000..a3aa8d0 --- /dev/null +++ b/typescript/src/renderer/styles/index.css @@ -0,0 +1,18 @@ +/* JSON-DOC Renderer Styles */ + +/* Import design tokens first */ +@import "./variables.css"; + +/* Import base styles */ +@import "./base.css"; + +/* Import component styles */ +@import "./typography.css"; +@import "./blocks.css"; +@import "./lists.css"; +@import "./table.css"; +@import "./media.css"; +@import "./layout.css"; + +/* Import responsive styles last */ +@import "./responsive.css"; diff --git a/typescript/src/renderer/styles/layout.css b/typescript/src/renderer/styles/layout.css new file mode 100644 index 0000000..2b282bd --- /dev/null +++ b/typescript/src/renderer/styles/layout.css @@ -0,0 +1,17 @@ +/* Layout Components */ + +/* Column Layout */ +.notion-column_list-block { + margin: var(--jsondoc-spacing-sm) 0; +} + +.notion-column-list { + display: flex; + gap: var(--jsondoc-column-gap); + width: 100%; +} + +.notion-column { + flex: 1; + min-width: 0; +} diff --git a/typescript/src/renderer/styles/lists.css b/typescript/src/renderer/styles/lists.css new file mode 100644 index 0000000..5beb2d6 --- /dev/null +++ b/typescript/src/renderer/styles/lists.css @@ -0,0 +1,44 @@ +/* List Styles */ + +/* List Container Blocks */ +.notion-unordered_list-block, +.notion-ordered_list-block { + margin: var(--jsondoc-spacing-xs) 0; + padding-left: 0; +} + +.notion-unordered_list-block { + list-style-type: disc; + padding-left: 1.5em; +} + +.notion-ordered_list-block { + list-style-type: decimal; + padding-left: 1.5em; +} + +/* List Items */ +.notion-bulleted_list-block, +.notion-numbered_list-block { + margin: 0; + padding: 1px 0; + line-height: var(--jsondoc-line-height-very-loose); + text-indent: var(--jsondoc-spacing-lg); + list-style-type: none; +} + +.notion-bulleted_list-block::before { + content: "•"; + margin: 0px; + padding: 0px; + text-indent: 0px; + font-size: var(--jsondoc-font-size-h3); + margin-right: var(--jsondoc-spacing-sm); +} + +.notion-checkbox { + width: var(--jsondoc-checkbox-size); + height: var(--jsondoc-checkbox-size); + margin: 0; + cursor: pointer; +} diff --git a/typescript/src/renderer/styles/media.css b/typescript/src/renderer/styles/media.css new file mode 100644 index 0000000..ba17b10 --- /dev/null +++ b/typescript/src/renderer/styles/media.css @@ -0,0 +1,44 @@ +/* Media and Image Styles */ + +.notion-image-block { + padding: var(--jsondoc-spacing-sm) var(--jsondoc-spacing-xs); +} + +.notion-image-placeholder { + width: 300px; + height: 200px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: var(--jsondoc-radius-md); + position: relative; + overflow: hidden; + margin: 10px 0; +} + +.notion-image-placeholder::before { + content: ""; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 60%; + background: linear-gradient(to top, #2c3e50 0%, #3498db 70%); + clip-path: polygon(0 100%, 30% 60%, 60% 80%, 100% 50%, 100% 100%); +} + +.notion-image-placeholder::after { + content: ""; + position: absolute; + top: 20px; + right: 30px; + width: 40px; + height: 40px; + background: #f1c40f; + border-radius: 50%; + box-shadow: var(--jsondoc-shadow-glow); +} + +.notion-image-caption { + color: var(--jsondoc-text-primary); + font-size: var(--jsondoc-font-size-caption); + margin-top: var(--jsondoc-spacing-md); +} diff --git a/typescript/src/renderer/styles/responsive.css b/typescript/src/renderer/styles/responsive.css new file mode 100644 index 0000000..4decf00 --- /dev/null +++ b/typescript/src/renderer/styles/responsive.css @@ -0,0 +1,32 @@ +/* Responsive Design */ + +@media (max-width: 768px) { + .json-doc-page { + padding: var(--jsondoc-spacing-2xl) var(--jsondoc-spacing-xl) + var(--jsondoc-page-bottom-padding); + } + + .json-doc-page-title { + font-size: 32px; + } + + .notion-column-list { + flex-direction: column; + gap: var(--jsondoc-spacing-md); + } +} + +@media (max-width: 480px) { + .json-doc-page { + padding: var(--jsondoc-spacing-xl) var(--jsondoc-spacing-lg) + var(--jsondoc-page-bottom-padding); + } + + .json-doc-page-title { + font-size: 28px; + } + + .json-doc-page-icon { + font-size: 60px; + } +} diff --git a/typescript/src/renderer/styles/table.css b/typescript/src/renderer/styles/table.css new file mode 100644 index 0000000..b97b70c --- /dev/null +++ b/typescript/src/renderer/styles/table.css @@ -0,0 +1,21 @@ +/* Table Styles */ + +.notion-table-block { + margin: var(--jsondoc-spacing-sm) 0; + width: 100%; +} + +.notion-table { + width: 100%; + border-collapse: collapse; + border-spacing: 0; + table-layout: fixed; +} + +.notion-table-row th, +.notion-table-row td { + border: 1px solid var(--jsondoc-border-medium); + padding: 6px var(--jsondoc-spacing-md); + vertical-align: top; + word-wrap: break-word; +} diff --git a/typescript/src/renderer/styles/typography.css b/typescript/src/renderer/styles/typography.css new file mode 100644 index 0000000..797066b --- /dev/null +++ b/typescript/src/renderer/styles/typography.css @@ -0,0 +1,88 @@ +/* Heading Blocks */ +.notion-header-block, +.notion-sub_header-block { + margin: 0px; +} + +.notion-header-block:first-child, +.notion-sub_header-block:first-child { + margin-top: 0; +} + +.notion-header-block h2 { + font-size: var(--jsondoc-font-size-h1); + margin: 0; + font-weight: var(--jsondoc-font-weight-semibold); + line-height: var(--jsondoc-line-height-relaxed); +} + +.notion-sub_header-block h3 { + font-size: var(--jsondoc-font-size-h2); + margin: 0; + font-weight: var(--jsondoc-font-weight-semibold); + line-height: var(--jsondoc-line-height-relaxed); +} + +.notion-sub_header-block h4 { + font-size: var(--jsondoc-font-size-h3); + margin: 0; + font-weight: var(--jsondoc-font-weight-semibold); + line-height: var(--jsondoc-line-height-relaxed); +} + +.notion-link { + color: inherit; + word-wrap: break-word; + cursor: pointer; + text-decoration: underline; + text-decoration-color: var(--jsondoc-text-muted); + transition: text-decoration-color var(--jsondoc-transition-fast); +} + +.notion-link:hover { + text-decoration-color: var(--jsondoc-text-primary); +} + +.notion-equation { + background: var(--jsondoc-bg-inline-code); + border-radius: var(--jsondoc-radius-sm); + padding: 0.2em 0.4em; + font-family: var(--jsondoc-font-family-serif); +} + +/* Text Colors */ +.notion-text-color-gray { + color: var(--jsondoc-color-gray); +} + +.notion-text-color-brown { + color: var(--jsondoc-color-brown); +} + +.notion-text-color-orange { + color: var(--jsondoc-color-orange); +} + +.notion-text-color-yellow { + color: var(--jsondoc-color-yellow); +} + +.notion-text-color-green { + color: var(--jsondoc-color-green); +} + +.notion-text-color-blue { + color: var(--jsondoc-color-blue); +} + +.notion-text-color-purple { + color: var(--jsondoc-color-purple); +} + +.notion-text-color-pink { + color: var(--jsondoc-color-pink); +} + +.notion-text-color-red { + color: var(--jsondoc-color-red); +} diff --git a/typescript/src/renderer/styles/variables.css b/typescript/src/renderer/styles/variables.css new file mode 100644 index 0000000..7e7319d --- /dev/null +++ b/typescript/src/renderer/styles/variables.css @@ -0,0 +1,122 @@ +/* JSON-DOC CSS Design Tokens */ +:root { + /* Colors */ + --jsondoc-text-primary: #37352f; + --jsondoc-text-secondary: rgba(55, 53, 47, 0.65); + --jsondoc-text-muted: rgba(55, 53, 47, 0.45); + --jsondoc-border-light: rgba(55, 53, 47, 0.16); + --jsondoc-border-medium: rgb(233, 233, 231); + + /* Background Colors */ + --jsondoc-bg-code: rgb(247, 246, 243); + --jsondoc-bg-inline-code: rgba(135, 131, 120, 0.15); + --jsondoc-bg-unsupported: rgba(255, 0, 0, 0.1); + + /* Text Colors */ + --jsondoc-color-gray: rgba(120, 119, 116, 1); + --jsondoc-color-brown: rgba(159, 107, 83, 1); + --jsondoc-color-orange: rgba(217, 115, 13, 1); + --jsondoc-color-yellow: rgba(203, 145, 47, 1); + --jsondoc-color-green: rgba(68, 131, 97, 1); + --jsondoc-color-blue: rgba(51, 126, 169, 1); + --jsondoc-color-purple: rgba(144, 101, 176, 1); + --jsondoc-color-pink: rgba(193, 76, 138, 1); + --jsondoc-color-red: rgba(212, 76, 71, 1); + + /* Accent Colors */ + --jsondoc-accent-inline-code: #eb5757; + --jsondoc-accent-checkbox: #0f7b0f; + --jsondoc-accent-error: #d32f2f; + + /* Typography */ + --jsondoc-font-family-sans: + -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, + "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; + --jsondoc-font-family-mono: + "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + --jsondoc-font-family-serif: "Times New Roman", serif; + + /* Font Sizes */ + --jsondoc-font-size-page-title: 40px; + --jsondoc-font-size-page-icon: 78px; + --jsondoc-font-size-h1: clamp(1.5rem, 4vw, 1.875rem); + --jsondoc-font-size-h2: clamp(1.25rem, 3vw, 1.5rem); + --jsondoc-font-size-h3: clamp(1.125rem, 2.5vw, 1.25rem); + --jsondoc-font-size-body: 1rem; + --jsondoc-font-size-caption: 14px; + --jsondoc-font-size-code: 14px; + --jsondoc-font-size-code-language: 12px; + --jsondoc-font-size-inline-code: 85%; + --jsondoc-font-size-equation: 1.2rem; + + /* Font Weights */ + --jsondoc-font-weight-normal: 400; + --jsondoc-font-weight-medium: 500; + --jsondoc-font-weight-semibold: 600; + --jsondoc-font-weight-bold: 700; + + /* Line Heights */ + --jsondoc-line-height-tight: 1.1; + --jsondoc-line-height-normal: 1.2; + --jsondoc-line-height-relaxed: 1.3; + --jsondoc-line-height-loose: 1.4; + --jsondoc-line-height-very-loose: 1.5; + + /* Spacing */ + --jsondoc-spacing-xs: 2px; + --jsondoc-spacing-sm: 4px; + --jsondoc-spacing-md: 8px; + --jsondoc-spacing-lg: 16px; + --jsondoc-spacing-xl: 24px; + --jsondoc-spacing-2xl: 48px; + --jsondoc-spacing-3xl: 96px; + + /* Border Radius */ + --jsondoc-radius-sm: 3px; + --jsondoc-radius-md: 8px; + + /* Shadows */ + --jsondoc-shadow-glow: 0 0 20px rgba(241, 196, 15, 0.3); + + /* Layout */ + --jsondoc-page-max-width: 100%; + --jsondoc-page-padding-desktop: var(--jsondoc-spacing-3xl); + --jsondoc-page-padding-mobile: var(--jsondoc-spacing-xl); + --jsondoc-page-bottom-padding: 30vh; + --jsondoc-column-gap: var(--jsondoc-spacing-lg); + + /* Interactive Elements */ + --jsondoc-checkbox-size: 14px; + --jsondoc-toggle-checkbox-size: 16px; + --jsondoc-icon-size: 16px; + + /* Transitions */ + --jsondoc-transition-fast: 0.2s ease; + + /* Z-Index Scale */ + --jsondoc-z-base: 1; + --jsondoc-z-elevated: 10; + --jsondoc-z-overlay: 100; + --jsondoc-z-modal: 1000; +} + +/* Theme Override Classes */ +.jsondoc-theme-light { + --jsondoc-text-primary: #37352f; + --jsondoc-text-secondary: rgba(55, 53, 47, 0.65); + --jsondoc-text-muted: rgba(55, 53, 47, 0.45); + --jsondoc-border-light: rgba(55, 53, 47, 0.16); + --jsondoc-border-medium: rgb(233, 233, 231); + --jsondoc-bg-code: rgb(247, 246, 243); + --jsondoc-bg-inline-code: rgba(135, 131, 120, 0.15); +} + +.jsondoc-theme-dark { + --jsondoc-text-primary: #ffffff; + --jsondoc-text-secondary: rgba(255, 255, 255, 0.65); + --jsondoc-text-muted: rgba(255, 255, 255, 0.45); + --jsondoc-border-light: rgba(255, 255, 255, 0.16); + --jsondoc-border-medium: rgba(255, 255, 255, 0.2); + --jsondoc-bg-code: rgba(255, 255, 255, 0.05); + --jsondoc-bg-inline-code: rgba(255, 255, 255, 0.1); +}