├── .husky └── pre-commit ├── .prettierignore ├── scripts ├── prepare └── generate-supabase-types ├── public ├── og.png └── icons │ ├── 192x192.png │ └── 512x512.png ├── assets ├── banner-dark.png ├── banner-light.png └── logo.svg ├── postcss.config.mjs ├── eslint.config.mjs ├── src ├── questions │ ├── question-4 │ │ ├── inquiry.mdx │ │ ├── explanation.mdx │ │ └── index.tsx │ ├── question-8 │ │ ├── inquiry.mdx │ │ └── index.tsx │ ├── question-13 │ │ ├── inquiry.mdx │ │ ├── explanation.mdx │ │ └── index.tsx │ ├── question-6 │ │ ├── inquiry.mdx │ │ ├── explanation.mdx │ │ └── index.tsx │ ├── question-2 │ │ ├── inquiry.mdx │ │ ├── index.ts │ │ └── explanation.mdx │ ├── question-16 │ │ ├── inquiry.mdx │ │ ├── index.tsx │ │ └── explanation.mdx │ ├── question-3 │ │ ├── inquiry.mdx │ │ ├── explanation.mdx │ │ └── index.ts │ ├── question-14 │ │ ├── inquiry.mdx │ │ ├── index.tsx │ │ └── explanation.mdx │ ├── question-10 │ │ ├── inquiry.mdx │ │ ├── index.tsx │ │ └── explanation.mdx │ ├── question-9 │ │ ├── inquiry.mdx │ │ ├── index.tsx │ │ └── explanation.mdx │ ├── question-11 │ │ ├── inquiry.mdx │ │ ├── index.tsx │ │ └── explanation.mdx │ ├── question-12 │ │ ├── inquiry.mdx │ │ ├── index.tsx │ │ └── explanation.mdx │ ├── shared-options.ts │ ├── Question.ts │ ├── question-1 │ │ ├── inquiry.mdx │ │ ├── index.tsx │ │ └── explanation.mdx │ ├── question-15 │ │ ├── index.tsx │ │ └── explanation.mdx │ ├── question-5 │ │ ├── explanation.mdx │ │ └── index.tsx │ ├── questions.ts │ └── question-7 │ │ └── index.tsx ├── app │ ├── review │ │ ├── layout.tsx │ │ ├── _components │ │ │ ├── explanation.tsx │ │ │ ├── option-wrapper.tsx │ │ │ ├── review.tsx │ │ │ └── review-option.tsx │ │ ├── page.tsx │ │ └── loading.tsx │ ├── quiz │ │ ├── [step] │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── _components │ │ │ └── question │ │ │ │ ├── question-progressbar.tsx │ │ │ │ ├── question-option.tsx │ │ │ │ └── question.tsx │ │ └── _actions │ │ │ ├── save-answers.action.ts │ │ │ └── submit-answer.ts │ ├── summary │ │ ├── layout.tsx │ │ ├── _components │ │ │ ├── skeleton.tsx │ │ │ ├── confetti.tsx │ │ │ ├── correct-answer-count.tsx │ │ │ ├── share-button.tsx │ │ │ ├── answers-provider.tsx │ │ │ └── summary-result.tsx │ │ ├── _actions │ │ │ └── getMyPercentile.ts │ │ ├── loading.tsx │ │ └── page.tsx │ ├── _components │ │ ├── layout.tsx │ │ ├── radio-label.tsx │ │ ├── restart-button.tsx │ │ ├── guard.tsx │ │ ├── quiz-header.tsx │ │ └── button.tsx │ ├── manifest.json │ ├── page.tsx │ ├── layout.tsx │ ├── globals.css │ └── icon.tsx ├── lib │ ├── mdx │ │ ├── code-block.ts │ │ ├── mdx-options.ts │ │ ├── shiki-options.ts │ │ ├── compileMdx.ts │ │ ├── add-container.ts │ │ ├── inline-code.tsx │ │ ├── white-space.ts │ │ └── fullstacksjs-theme.ts │ ├── cn.tsx │ ├── supabase │ │ ├── config.ts │ │ ├── createBrowserClient.ts │ │ ├── supabase-error.ts │ │ ├── createServerClient.ts │ │ ├── createClient.ts │ │ ├── supabase.ts │ │ └── Database.ts │ └── posthog │ │ ├── config.ts │ │ ├── createPostHogClient.ts │ │ └── PostHogProvider.tsx ├── state │ └── useAnswers.ts └── mdx-components.tsx ├── pnpm-workspace.yaml ├── supabase ├── migrations │ ├── 20250411234738_index.sql │ ├── 20251209091950_add_correct_answers.sql │ ├── 20250411215717_rank.sql │ └── 20250320080206_init.sql ├── .gitignore └── config.toml ├── .lintstagedrc.json ├── .env.example ├── .editorconfig ├── .prettierrc ├── cspell.json ├── .gitignore ├── tsconfig.json ├── LICENSE ├── next.config.ts ├── README.md └── package.json /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | lint-staged 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | src/lib/supabase/Database.ts 3 | -------------------------------------------------------------------------------- /scripts/prepare: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | npx --no-install husky 4 | -------------------------------------------------------------------------------- /public/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstacksjs/you-dont-know-html/HEAD/public/og.png -------------------------------------------------------------------------------- /assets/banner-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstacksjs/you-dont-know-html/HEAD/assets/banner-dark.png -------------------------------------------------------------------------------- /assets/banner-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstacksjs/you-dont-know-html/HEAD/assets/banner-light.png -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { plugins: { "@tailwindcss/postcss": {} } }; 2 | export default config; 3 | -------------------------------------------------------------------------------- /public/icons/192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstacksjs/you-dont-know-html/HEAD/public/icons/192x192.png -------------------------------------------------------------------------------- /public/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstacksjs/you-dont-know-html/HEAD/public/icons/512x512.png -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@fullstacksjs/eslint-config"; 2 | 3 | export default defineConfig({ tailwind: false }); 4 | -------------------------------------------------------------------------------- /src/questions/question-4/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What does this code do? 2 | 3 | ```html 4 | 5 | ``` 6 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - core-js 3 | - esbuild 4 | - sharp 5 | - supabase 6 | - unrs-resolver 7 | 8 | savePrefix: "" 9 | -------------------------------------------------------------------------------- /supabase/migrations/20250411234738_index.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX idx_answer_game_id ON answer(game_id); 2 | CREATE INDEX idx_answer_is_correct ON answer(is_correct); 3 | -------------------------------------------------------------------------------- /supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | 5 | # dotenvx 6 | .env.keys 7 | .env.local 8 | .env.*.local 9 | 10 | src/lib/supabase/Database.ts 11 | -------------------------------------------------------------------------------- /src/questions/question-8/inquiry.mdx: -------------------------------------------------------------------------------- 1 | How should the following HTML be written to properly display the event date? 2 | 3 | ```html 4 |

The event took place on June 15.

5 | ``` 6 | -------------------------------------------------------------------------------- /src/questions/question-13/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What will be the output of the following code? 2 | 3 | ```html-derivative 4 | placeholder 5 | ``` 6 | -------------------------------------------------------------------------------- /src/app/review/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Layout } from "../_components/layout"; 2 | 3 | export default function Page({ children }: React.PropsWithChildren) { 4 | return {children}; 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/mdx/code-block.ts: -------------------------------------------------------------------------------- 1 | import dedent from "dedent"; 2 | 3 | export const codeBlock = (s: TemplateStringsArray) => { 4 | return `\`\`\`html-derivative\n${dedent`${s.join("\n")}`}\n\`\`\``; 5 | }; 6 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.{md,json,yml,yaml,html}": [ 3 | "prettier --write", 4 | "cspell --no-must-find-files" 5 | ], 6 | "*.{ts,tsx}": ["eslint --fix", "cspell --no-must-find-files"] 7 | } 8 | -------------------------------------------------------------------------------- /src/app/quiz/[step]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Layout } from "../../_components/layout"; 2 | 3 | export default function Page({ children }: React.PropsWithChildren) { 4 | return {children}; 5 | } 6 | -------------------------------------------------------------------------------- /src/questions/question-6/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What is the correct HTML to make the following text semantically accurate? 2 | 3 | ```html-derivative 4 |

The old price was $49.99, but now it's just $29.99!

5 | ``` 6 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Supabase (can get them from your local Supabase instance) 2 | NEXT_PUBLIC_SUPABASE_URL= 3 | NEXT_PUBLIC_SUPABASE_ANON_KEY= 4 | 5 | # Posthog 6 | NEXT_PUBLIC_POSTHOG_HOST= 7 | NEXT_PUBLIC_POSTHOG_KEY= 8 | -------------------------------------------------------------------------------- /scripts/generate-supabase-types: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | OUT=./src/lib/supabase/Database.ts 5 | 6 | npx --no-install supabase gen types --local >$OUT 7 | echo -e "/* eslint-disable */\n$(cat $OUT)" >$OUT 8 | -------------------------------------------------------------------------------- /src/questions/question-2/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What is the color of the "FullstacksJS" text in the following code? 2 | 3 | ```html 4 |
5 |

6 | FullstacksJS 7 |

8 | ``` 9 | -------------------------------------------------------------------------------- /src/questions/question-16/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What will be the output of the following code? 2 | 3 | ```html 4 |

5 | 6 | 9 | ``` 10 | -------------------------------------------------------------------------------- /src/lib/cn.tsx: -------------------------------------------------------------------------------- 1 | import type { ClassValue } from "clsx"; 2 | 3 | import { clsx } from "clsx"; 4 | import { twMerge } from "tailwind-merge"; 5 | 6 | export const cn = (...inputs: ClassValue[]) => { 7 | return twMerge(clsx(inputs)); 8 | }; 9 | -------------------------------------------------------------------------------- /src/questions/question-3/inquiry.mdx: -------------------------------------------------------------------------------- 1 | Which of the following is the best way to link an accessible error message to a form field? 2 | 3 | ```html 4 |

5 | 6 | Error: Enter a valid email address 7 |

8 | ``` 9 | -------------------------------------------------------------------------------- /src/questions/question-14/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What is the color of the "FullstacksJS" text in the following HTML? 2 | 3 | ```html 4 |
5 | 6 | FullstacksJS 7 | 8 |
9 | ``` 10 | -------------------------------------------------------------------------------- /src/questions/question-10/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What is the color of the "FullstacksJS" text in the following code? 2 | 3 | ```html 4 |
5 |

6 |

FullstacksJS 7 |

8 |

9 | ``` 10 | -------------------------------------------------------------------------------- /src/questions/question-9/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What is the color of the "FullstacksJS" text in the following code? 2 | 3 | ```html 4 |
5 |
6 |
FullstacksJS 7 |
8 |
9 | ``` 10 | -------------------------------------------------------------------------------- /src/app/summary/layout.tsx: -------------------------------------------------------------------------------- 1 | export default function SummaryLayout({ children }: React.PropsWithChildren) { 2 | return ( 3 |
4 | {children} 5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/questions/question-11/inquiry.mdx: -------------------------------------------------------------------------------- 1 | What is the color of the "FullstacksJS" text in the following code? 2 | 3 | ```html 4 | 8 |
9 |

FullstacksJS

10 |
11 | ``` 12 | -------------------------------------------------------------------------------- /src/lib/mdx/mdx-options.ts: -------------------------------------------------------------------------------- 1 | import type { NextMDXOptions } from "@next/mdx"; 2 | 3 | import rehypeShiki from "@shikijs/rehype"; 4 | 5 | import { shikiOptions } from "./shiki-options"; 6 | 7 | export const mdxOptions: NextMDXOptions["options"] = { 8 | rehypePlugins: [[rehypeShiki, shikiOptions]], 9 | }; 10 | -------------------------------------------------------------------------------- /src/questions/question-12/inquiry.mdx: -------------------------------------------------------------------------------- 1 | How many columns are there in the following table? 2 | 3 | ```html 4 | 5 | 7 | 8 | 10 | 11 | 13 |
Caption 6 |
123 9 |
123 12 |
14 | ``` 15 | -------------------------------------------------------------------------------- /src/lib/supabase/config.ts: -------------------------------------------------------------------------------- 1 | import { Config } from "@fullstacksjs/config"; 2 | 3 | export const config = new Config({ 4 | url: Config.string().required(), 5 | anonKey: Config.string().required(), 6 | }).parse({ 7 | url: process.env.NEXT_PUBLIC_SUPABASE_URL, 8 | anonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, 9 | }); 10 | -------------------------------------------------------------------------------- /src/app/summary/_components/skeleton.tsx: -------------------------------------------------------------------------------- 1 | export function Skeleton() { 2 | return ( 3 |
4 | 5 | 6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /src/app/_components/layout.tsx: -------------------------------------------------------------------------------- 1 | export const Layout = ({ children }: { children: React.ReactNode }) => { 2 | return ( 3 |
4 |
5 | {children} 6 |
7 |
8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /src/questions/shared-options.ts: -------------------------------------------------------------------------------- 1 | import type { Question } from "./Question"; 2 | 3 | export const IDontKnow = { 4 | id: 127, 5 | text: () => "I don't know", 6 | } satisfies Question["options"][number]; 7 | 8 | export const InvalidHTML = { 9 | id: 128, 10 | text: () => "It's not a valid HTML syntax", 11 | } satisfies Question["options"][number]; 12 | -------------------------------------------------------------------------------- /src/lib/supabase/createBrowserClient.ts: -------------------------------------------------------------------------------- 1 | import { createBrowserClient } from "@supabase/ssr"; 2 | 3 | import type { Database } from "./Database"; 4 | 5 | export function createBrowserSupabaseClient() { 6 | return createBrowserClient( 7 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 8 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/questions/Question.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentType } from "react"; 2 | 3 | type ID = number; 4 | 5 | export interface QuestionOption { 6 | id: ID; 7 | text: ComponentType; 8 | } 9 | 10 | export interface Question { 11 | id: ID; 12 | inquiry: ComponentType; 13 | options: QuestionOption[]; 14 | explanation: ComponentType; 15 | correctAnswerId: ID; 16 | } 17 | -------------------------------------------------------------------------------- /src/questions/question-1/inquiry.mdx: -------------------------------------------------------------------------------- 1 | Which tags should be used for \<1> 2 | and \<2> to make the following HTML valid? 3 | 4 | ```html-derivative 5 |

6 | To print your name in the command line, 7 | run <1>echo "Hello, FullstacksJS" 8 |

9 |

10 | Result: 11 | <2 name="result">Hello, FullstacksJS 12 |

13 | ``` 14 | -------------------------------------------------------------------------------- /supabase/migrations/20251209091950_add_correct_answers.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."game" add column "correct_answers" smallint; 2 | 3 | update game 4 | set correct_answers = coalesce(a.correct_answers_count, 0) 5 | from ( 6 | select game_id, count(*) as correct_answers_count 7 | from answer 8 | where answer.is_correct = true 9 | group by game_id 10 | ) a 11 | where game.id = a.game_id; 12 | 13 | -------------------------------------------------------------------------------- /src/lib/posthog/config.ts: -------------------------------------------------------------------------------- 1 | import { Config } from "@fullstacksjs/config"; 2 | 3 | export const configSchema = new Config({ 4 | key: Config.string(), 5 | host: Config.string(), 6 | apiHost: Config.string(), 7 | uiHost: Config.string(), 8 | }).parse({ 9 | key: process.env.NEXT_PUBLIC_POSTHOG_KEY, 10 | host: process.env.NEXT_PUBLIC_POSTHOG_HOST, 11 | apiHost: "/ingest", 12 | uiHost: "https://eu.posthog.com", 13 | }); 14 | -------------------------------------------------------------------------------- /src/lib/posthog/createPostHogClient.ts: -------------------------------------------------------------------------------- 1 | import "server-only"; 2 | import { PostHog } from "posthog-node"; 3 | 4 | import { configSchema } from "./config"; 5 | 6 | export default function createPostHogClient() { 7 | const key = configSchema.get("key"); 8 | if (!key) return undefined; 9 | 10 | return new PostHog(key, { 11 | host: configSchema.get("host"), 12 | flushAt: 1, 13 | flushInterval: 0, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /src/app/summary/_actions/getMyPercentile.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { questions } from "@app/questions/questions"; 4 | import { getGamesCountWithCorrectAnswers } from "@app/supabase/supabase"; 5 | import { percent } from "@fullstacksjs/toolbox"; 6 | 7 | export async function getMyPercentile(correctAnswers: number) { 8 | const gamesCount = await getGamesCountWithCorrectAnswers(correctAnswers); 9 | return percent(gamesCount, questions.length); 10 | } 11 | -------------------------------------------------------------------------------- /src/app/quiz/_components/question/question-progressbar.tsx: -------------------------------------------------------------------------------- 1 | import { questions } from "@app/questions/questions"; 2 | 3 | interface Props { 4 | currentStep: number; 5 | } 6 | 7 | export const QuestionProgressbar = ({ currentStep }: Props) => { 8 | return ( 9 |
13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /supabase/migrations/20250411215717_rank.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION get_games_with_correct_answers(min_correct_answers integer) 2 | RETURNS TABLE (game_id bigint, correct_answers_count bigint) 3 | LANGUAGE sql 4 | AS $$ 5 | SELECT 6 | g.id as game_id, 7 | COUNT(a.id) as correct_answers_count 8 | FROM game g 9 | JOIN answer a ON a.game_id = g.id 10 | WHERE a.is_correct = true 11 | GROUP BY g.id 12 | HAVING COUNT(a.id) <= min_correct_answers; 13 | $$; 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "endOfLine": "lf", 5 | "htmlWhitespaceSensitivity": "css", 6 | "insertPragma": false, 7 | "bracketSameLine": false, 8 | "jsxSingleQuote": false, 9 | "printWidth": 80, 10 | "proseWrap": "always", 11 | "quoteProps": "consistent", 12 | "requirePragma": false, 13 | "semi": true, 14 | "singleQuote": false, 15 | "tabWidth": 2, 16 | "trailingComma": "all", 17 | "useTabs": false 18 | } 19 | -------------------------------------------------------------------------------- /src/app/_components/radio-label.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@app/cn"; 2 | 3 | export const RadioLabel = ({ 4 | className, 5 | ...props 6 | }: React.ComponentProps<"label">) => { 7 | return ( 8 | // eslint-disable-next-line jsx-a11y/label-has-associated-control 9 |
31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/questions/question-7/index.tsx: -------------------------------------------------------------------------------- 1 | import { compileMDX } from "@app/mdx/compileMdx"; 2 | 3 | import type { Question } from "../Question"; 4 | 5 | import { IDontKnow } from "../shared-options"; 6 | 7 | export default { 8 | id: 7, 9 | inquiry: await compileMDX( 10 | "Which of the following best describes the purpose of the {''} element in HTML?", 11 | ), 12 | options: [ 13 | { 14 | id: 1, 15 | text: () => 16 | "It displays a progress bar for tasks like file uploads or downloads.", 17 | }, 18 | { 19 | id: 2, 20 | text: () => 21 | "It defines a scalar measurement within a known range, such as a score, battery level, or disk usage.", 22 | }, 23 | { id: 3, text: () => "It collects numeric input from users in a form." }, 24 | { 25 | id: 4, 26 | text: () => 27 | "It visually styles numeric text using CSS based on its value.", 28 | }, 29 | IDontKnow, 30 | ], 31 | explanation: await compileMDX( 32 | "> The {''} HTML element represents either a scalar value within a known range or a fractional value. [Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)", 33 | ), 34 | correctAnswerId: 2, 35 | } satisfies Question; 36 | -------------------------------------------------------------------------------- /src/app/review/_components/option-wrapper.tsx: -------------------------------------------------------------------------------- 1 | import type { QuestionOption } from "@app/questions/Question"; 2 | 3 | import { questions } from "@app/questions/questions"; 4 | import { getAnswersCount, getGamesCount } from "@app/supabase/supabase"; 5 | import { isNull, percent } from "@fullstacksjs/toolbox"; 6 | 7 | import { ReviewOption } from "./review-option"; 8 | 9 | interface Props { 10 | option: QuestionOption; 11 | questionId: number; 12 | } 13 | 14 | export async function OptionWrapper({ option, questionId }: Props) { 15 | const gamesCount = await getGamesCount(questionId); 16 | const answersCount = await getAnswersCount(questionId, option.id); 17 | const correctAnswerId = questions.find( 18 | (q) => q.id === questionId, 19 | )?.correctAnswerId; 20 | 21 | if (isNull(correctAnswerId)) return null; 22 | 23 | const percentage = percent(answersCount, gamesCount); 24 | 25 | return ( 26 |
  • 27 | 33 | 34 | 35 | 36 | 37 |
  • 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/app/review/_components/review.tsx: -------------------------------------------------------------------------------- 1 | import type { Question } from "@app/questions/Question"; 2 | 3 | import { questions } from "@app/questions/questions"; 4 | 5 | import { Guard } from "../../_components/guard"; 6 | import { Explanation } from "./explanation"; 7 | import { OptionWrapper } from "./option-wrapper"; 8 | 9 | interface Props { 10 | question: Question; 11 | } 12 | 13 | export function Review({ question }: Props) { 14 | return ( 15 | 16 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/questions/question-3/index.ts: -------------------------------------------------------------------------------- 1 | import { codeBlock } from "@app/mdx/code-block"; 2 | import { compileMDX } from "@app/mdx/compileMdx"; 3 | 4 | import type { Question } from "../Question"; 5 | 6 | import { IDontKnow } from "../shared-options"; 7 | import explanation from "./explanation.mdx"; 8 | import inquiry from "./inquiry.mdx"; 9 | 10 | export default { 11 | id: 3, 12 | inquiry, 13 | options: [ 14 | { 15 | id: 3, 16 | text: await compileMDX(codeBlock` 17 |

    18 | 19 | Error: Enter a valid email address 20 |

    21 | `), 22 | }, 23 | { 24 | id: 1, 25 | text: await compileMDX(codeBlock` 26 |

    27 | 28 | Error: Enter a valid email address 29 |

    30 | `), 31 | }, 32 | { 33 | id: 2, 34 | text: await compileMDX(codeBlock` 35 |

    36 | 37 | Error: Enter a valid email address 38 |

    39 | `), 40 | }, 41 | IDontKnow, 42 | ], 43 | explanation, 44 | correctAnswerId: 1, 45 | } satisfies Question; 46 | -------------------------------------------------------------------------------- /src/questions/question-16/explanation.mdx: -------------------------------------------------------------------------------- 1 | The parser automatically closes the first `p` tag when facing the next opening `p` tag. 2 | 3 | ### Why? 4 | There's a rule for that in the spec: 5 | 6 | > A `p` element's end tag may be omitted if the `p` element is immediately followed by an 7 | > `address`, `article`, `p`, `aside`, [...] or if there is no more content in the parent element and the parent element is an HTML element that is not an 8 | > `a`, `audio`, `del`, [...], `ins` element, or an autonomous custom element. [Spec reference](https://html.spec.whatwg.org/multipage/syntax.html#normal-elements) 9 | 10 | Then the parser will create the second `p` element for `

    `. 11 | 12 | And finally the parser will create the third `p` element when it encounters the closing `

    ` tag. 13 | 14 | ### Why? 15 | 16 | When the parser is in [in body insertion mode](https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody), and encounters a token for closing `p` tag, it must follow this algorithm: 17 | 18 | > **An end tag whose tag name is `p`** 19 | > 20 | > If the stack of open elements does not have a `p` element in button scope, then this is a parse error; 21 | > insert an HTML element for a `p` start tag token with no attributes. 22 | > 23 | > Close a `p` element. ([I don't recommend opening this link](https://html.spec.whatwg.org/multipage/parsing.html#close-a-p-element)) 24 | 25 | -------------------------------------------------------------------------------- /src/app/quiz/[step]/page.tsx: -------------------------------------------------------------------------------- 1 | import { questions } from "@app/questions/questions"; 2 | import { notFound } from "next/navigation"; 3 | 4 | import { Question } from "../_components/question/question"; 5 | import { QuestionProgressbar } from "../_components/question/question-progressbar"; 6 | import { QuizHeader } from "../../_components/quiz-header"; 7 | 8 | export function generateStaticParams() { 9 | return questions.map((_, index) => ({ step: String(index + 1) })); 10 | } 11 | 12 | interface Props { 13 | params: Promise<{ step: string }>; 14 | } 15 | 16 | export default async function QuizPage({ params }: Props) { 17 | const step = Number((await params).step); 18 | const currentStep = step - 1; 19 | const currentQuestion = questions[currentStep]; 20 | 21 | if (!currentQuestion) return notFound(); 22 | 23 | return ( 24 | <> 25 | (s === 0 ? "/" : `/quiz/${s}`)} 27 | getLabel={(s) => `Question ${s}/${questions.length}`} 28 | step={step} 29 | /> 30 | 31 | } 34 | step={step} 35 | isLastQuestion={step === questions.length} 36 | options={currentQuestion.options.map((option) => ({ 37 | id: option.id, 38 | text: , 39 | }))} 40 | /> 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/questions/question-6/index.tsx: -------------------------------------------------------------------------------- 1 | import { codeBlock } from "@app/mdx/code-block"; 2 | import { compileMDX } from "@app/mdx/compileMdx"; 3 | 4 | import type { Question } from "../Question"; 5 | 6 | import { IDontKnow } from "../shared-options"; 7 | import explanation from "./explanation.mdx"; 8 | import inquiry from "./inquiry.mdx"; 9 | 10 | export default { 11 | id: 6, 12 | inquiry, 13 | options: [ 14 | { 15 | id: 1, 16 | text: await compileMDX( 17 | codeBlock` 18 |

    19 | The old price was $49.99, but now it's just $29.99 20 |

    21 | `, 22 | ), 23 | }, 24 | { 25 | id: 2, 26 | text: await compileMDX( 27 | codeBlock` 28 |

    29 | The old price was $49.99, but now it's just $29.99 30 |

    31 | `, 32 | ), 33 | }, 34 | { 35 | id: 3, 36 | text: await compileMDX( 37 | codeBlock` 38 |

    39 | The old price was $49.99, but now it's just $29.99 40 |

    41 | `, 42 | ), 43 | }, 44 | { 45 | id: 4, 46 | text: await compileMDX( 47 | codeBlock` 48 |

    49 | The old price was $49.99, but now it's just $29.99 50 |

    51 | `, 52 | ), 53 | }, 54 | IDontKnow, 55 | ], 56 | explanation, 57 | correctAnswerId: 4, 58 | } satisfies Question; 59 | -------------------------------------------------------------------------------- /src/questions/question-5/index.tsx: -------------------------------------------------------------------------------- 1 | import { codeBlock } from "@app/mdx/code-block"; 2 | import { compileMDX } from "@app/mdx/compileMdx"; 3 | 4 | import type { Question } from "../Question"; 5 | 6 | import { IDontKnow } from "../shared-options"; 7 | import explanation from "./explanation.mdx"; 8 | 9 | export default { 10 | id: 5, 11 | inquiry: () => 12 | 'What is the correct way to define the term "CSS" in HTML using semantic markup?', 13 | options: [ 14 | { 15 | id: 1, 16 | text: await compileMDX( 17 | codeBlock` 18 |

    19 | CSS controls web page styles. 20 |

    `, 21 | ), 22 | }, 23 | { 24 | id: 2, 25 | text: await compileMDX( 26 | codeBlock` 27 |

    28 | CSS 29 | controls web page styles. 30 |

    `, 31 | ), 32 | }, 33 | { 34 | id: 3, 35 | text: await compileMDX( 36 | codeBlock` 37 |

    38 | CSS controls web page styles. 39 |

    `, 40 | ), 41 | }, 42 | { 43 | id: 4, 44 | text: await compileMDX( 45 | codeBlock` 46 | 47 | CSS controls web page styles. 48 | `, 49 | ), 50 | }, 51 | IDontKnow, 52 | ], 53 | explanation, 54 | correctAnswerId: 2, 55 | } satisfies Question; 56 | -------------------------------------------------------------------------------- /src/lib/posthog/PostHogProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { usePathname, useSearchParams } from "next/navigation"; 4 | import posthog from "posthog-js"; 5 | import { 6 | PostHogProvider as BasePostHogProvider, 7 | usePostHog, 8 | } from "posthog-js/react"; 9 | import { Suspense, useEffect } from "react"; 10 | 11 | import { configSchema } from "./config"; 12 | 13 | export function PostHogProvider({ children }: { children: React.ReactNode }) { 14 | useEffect(() => { 15 | const { key, apiHost, uiHost } = configSchema.getAll(); 16 | if (!key) return; 17 | 18 | posthog.init(key, { 19 | api_host: apiHost, 20 | ui_host: uiHost, 21 | capture_pageview: false, 22 | capture_pageleave: true, 23 | }); 24 | }, []); 25 | 26 | return ( 27 | 28 | 29 | 30 | 31 | {children} 32 | 33 | ); 34 | } 35 | 36 | function PostHogPageView() { 37 | const pathname = usePathname(); 38 | const searchParams = useSearchParams(); 39 | const posthogClient = usePostHog(); 40 | 41 | useEffect(() => { 42 | if (!pathname) return; 43 | 44 | const url = new URL(pathname, window.location.origin); 45 | 46 | if (searchParams.toString()) { 47 | url.search = searchParams.toString(); 48 | } 49 | 50 | posthogClient.capture("$pageview", { $current_url: url }); 51 | }, [pathname, searchParams, posthogClient]); 52 | 53 | return null; 54 | } 55 | -------------------------------------------------------------------------------- /src/mdx-components.tsx: -------------------------------------------------------------------------------- 1 | import type { MDXComponents } from "mdx/types"; 2 | 3 | import { InlineCode } from "./lib/mdx/inline-code"; 4 | 5 | export function useMDXComponents(components: MDXComponents): MDXComponents { 6 | return { 7 | ...components, 8 | blockquote: ({ children }) => ( 9 |
    10 | {children} 11 |
    12 | ), 13 | h3: ({ children }) =>

    {children}

    , 14 | a: ({ children, ...props }) => ( 15 | 22 | {children} 23 | 24 | ), 25 | ol: ({ children }) => ( 26 |
      {children}
    27 | ), 28 | ul: ({ children }) =>
      {children}
    , 29 | li: ({ children }) => ( 30 |
  • {children}
  • 31 | ), 32 | hr: () =>
    , 33 | H: ({ children }) => { 34 | return {children}; 35 | }, 36 | code: (props) => , 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | 3 | import type { Metadata, Viewport } from "next"; 4 | 5 | import { PostHogProvider } from "@app/posthog/PostHogProvider"; 6 | import { Fira_Mono as FiraMono, Ubuntu } from "next/font/google"; 7 | 8 | const inter = Ubuntu({ 9 | variable: "--font-inter", 10 | subsets: ["latin"], 11 | weight: ["400", "700"], 12 | style: ["italic", "normal"], 13 | }); 14 | 15 | const firaMono = FiraMono({ 16 | variable: "--font-fira-mono", 17 | subsets: ["latin"], 18 | weight: ["500"], 19 | }); 20 | 21 | const title = "You don't know html"; 22 | const description = "If you think you know HTML, think again."; 23 | const images = { url: "/og.png", alt: title }; 24 | 25 | export const metadata: Metadata = { 26 | title, 27 | description, 28 | keywords: ["html", "quiz", "challenge"], 29 | metadataBase: new URL("https://youdontknowhtml.com"), 30 | openGraph: { 31 | description, 32 | images, 33 | title, 34 | }, 35 | twitter: { 36 | images, 37 | title, 38 | card: "summary_large_image", 39 | }, 40 | }; 41 | 42 | export const viewport: Viewport = { 43 | themeColor: "#23252e", 44 | colorScheme: "dark", 45 | }; 46 | 47 | export default function RootLayout({ children }: React.PropsWithChildren) { 48 | return ( 49 | 53 | 54 | {children} 55 | 56 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /src/questions/question-12/explanation.mdx: -------------------------------------------------------------------------------- 1 | > Certain tags can be omitted.[Spec Reference](https://html.spec.whatwg.org/multipage/syntax.html#optional-tags) 2 | > 3 | > - A `caption` element's end tag may be omitted if the caption element is not immediately followed by ASCII whitespace or a comment. 4 | > - A `colgroup` element's end tag may be omitted if the colgroup element is not immediately followed by ASCII whitespace or a comment. 5 | > - A `tr` element's end tag may be omitted if the tr element is immediately followed by another `tr` element, or if there is no more content in the parent element. 6 | > - A `th` element's end tag may be omitted if the th element is immediately followed by a `td` or `th` element, or if there is no more content in the parent element. 7 | > - A `td` element's end tag may be omitted if the td element is immediately followed by a `td` or `th` element, or if there is no more content in the parent element. 8 | 9 | > `col` is a void element, so it doesn't need a closing tag. [Spec Reference](https://html.spec.whatwg.org/multipage/tables.html#the-col-element) 10 | 11 | So the code is a valid HTML syntax and is equivalent to: 12 | 13 | ```html 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
    Caption
    123
    123
    34 | ``` 35 | 36 | The table has 4 columns. 37 | -------------------------------------------------------------------------------- /src/app/summary/_components/share-button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { use } from "react"; 4 | import { useIsClient } from "usehooks-ts"; 5 | 6 | import { Button } from "../../_components/button"; 7 | import { AnswersContext } from "./answers-provider"; 8 | 9 | function getTweetText(score: number, total: number) { 10 | if (score === 0) { 11 | return `I scored 0/${total} on "You Don't Know HTML" 😅 Apparently, I *really* don't. Try it and do better:`; 12 | } else if (score === total) { 13 | return `I got a perfect ${total}/${total} on "You Don't Know HTML" 🔥 Guess I actually *do* know HTML! Think you can match me?`; 14 | } else { 15 | return `I scored ${score}/${total} on "You Don't Know HTML"! Not bad, but can you do better? 👀`; 16 | } 17 | } 18 | 19 | export const ShareButton = () => { 20 | const { correctAnswers, total } = use(AnswersContext); 21 | const isClient = useIsClient(); 22 | 23 | const shareData = { 24 | text: getTweetText(correctAnswers, total), 25 | url: isClient ? window.location.origin : "", 26 | hashtags: "youdontknowhtml", 27 | }; 28 | 29 | return ( 30 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/app/quiz/_components/question/question-option.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type { ReactNode } from "react"; 4 | 5 | import { cn } from "@app/cn"; 6 | import { noop } from "@fullstacksjs/toolbox"; 7 | import { usePress } from "react-aria"; 8 | 9 | import { RadioLabel } from "../../../_components/radio-label"; 10 | 11 | interface Props { 12 | children: ReactNode; 13 | id: number; 14 | onSelect?: (id: number) => void; 15 | questionId: number; 16 | checked?: boolean; 17 | disabled?: boolean; 18 | } 19 | 20 | export function QuestionOption({ 21 | children, 22 | questionId, 23 | id, 24 | checked, 25 | disabled, 26 | onSelect, 27 | ...props 28 | }: Props) { 29 | const { pressProps } = usePress({ 30 | onPress: () => onSelect?.(id), 31 | isDisabled: disabled, 32 | }); 33 | 34 | return ( 35 | 44 |
    45 | 52 |
    53 |
    54 | {children} 55 | 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![banner](https://github.com/fullstacksjs/you-dont-know-html/blob/main/assets/banner-dark.png?raw=true#gh-dark-mode-only) 2 | ![banner](https://github.com/fullstacksjs/you-dont-know-html/blob/main/assets/banner-light.png?raw=true#gh-light-mode-only) 3 | 4 | # You don't know HTML 5 | 6 | Think you know HTML? [Take the test](https://youdontknowhtml.com)! This interactive quiz challenges your 7 | understanding of HTML language. 8 | 9 | ## Contributing 10 | 11 | ### Prerequisites 12 | 13 | - Node.js (version specified in package.json) 14 | - pnpm (version specified in package.json) 15 | - Docker 16 | 17 | ### Development 18 | 19 | 1. Clone the repository: 20 | 2. Install dependencies: 21 | 3. Start the Supabase server 22 | ```bash 23 | npm run supabase:start 24 | ``` 25 | 4. Set up environment variables: 26 | ```bash 27 | cp .env.example .env 28 | ``` 29 | 5. Set variables for your local Supabase instance. 30 | 6. Start the development server: 31 | ```bash 32 | pnpm dev 33 | ``` 34 | 35 | ## Making new changes 36 | 37 | Contributions are welcome! Whether it's adding new questions, improving 38 | explanations, or fixing bugs, please feel free to submit a pull request. 39 | 40 | 1. Fork the repository 41 | 2. Create your feature branch (`git checkout -b feature/amazing-question`) 42 | 3. Commit your changes (`git commit -m 'feat: add a new question'`) 43 | 4. Open a Pull Request 44 | 45 | ## Tech Stack 46 | 47 | - [Next.js](https://nextjs.org/) - React framework 48 | - [Tailwind CSS](https://tailwindcss.com/) - Styling 49 | - [MDX](https://mdxjs.com/) - Content 50 | - [Supabase](https://supabase.com/) - Backend 51 | -------------------------------------------------------------------------------- /src/app/summary/_components/answers-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type { Question } from "@app/questions/Question"; 4 | 5 | import { useUserAnswers } from "@app/state/useAnswers"; 6 | import { isNull } from "@fullstacksjs/toolbox"; 7 | import { createContext, useEffect, useMemo, useState } from "react"; 8 | import { useIsClient } from "usehooks-ts"; 9 | 10 | export const AnswersContext = createContext<{ 11 | correctAnswers: number; 12 | loading: boolean; 13 | total: number; 14 | }>({ 15 | correctAnswers: 0, 16 | total: 0, 17 | loading: true, 18 | }); 19 | AnswersContext.displayName = "AnswersContext"; 20 | 21 | export const AnswersProvider = ({ 22 | children, 23 | questions, 24 | }: { 25 | children: React.ReactNode; 26 | questions: Pick[]; 27 | }) => { 28 | const isClient = useIsClient(); 29 | const answers = useUserAnswers(); 30 | const [correctAnswers, setCorrectAnswers] = useState(); 31 | const loading = !isClient || isNull(correctAnswers); 32 | 33 | useEffect(() => { 34 | // eslint-disable-next-line react-hooks/set-state-in-effect, @eslint-react/hooks-extra/no-direct-set-state-in-use-effect 35 | setCorrectAnswers( 36 | questions.filter((q) => answers[q.id] === q.correctAnswerId).length, 37 | ); 38 | }, [answers, questions]); 39 | 40 | const value = useMemo( 41 | () => ({ 42 | correctAnswers: correctAnswers ?? 0, 43 | loading, 44 | total: questions.length, 45 | }), 46 | [correctAnswers, loading, questions.length], 47 | ); 48 | 49 | return {children}; 50 | }; 51 | -------------------------------------------------------------------------------- /src/questions/question-9/explanation.mdx: -------------------------------------------------------------------------------- 1 | This one is a bit tricky, but here's the breakdown: 2 | 3 | ### Understanding “normal elements” and end tag omission 4 | 5 | In HTML, elements like {'
    '} are considered normal elements. According to the spec: 6 | 7 | > The start and end tags of certain normal elements can be omitted. [Spec reference](https://html.spec.whatwg.org/multipage/syntax.html#normal-elements) 8 | 9 | However, for {'
    '}, the element definition clearly states: 10 | 11 | > Tag omission in text/html: 12 | > 13 | > Neither tag is omissible. [Spec reference](https://html.spec.whatwg.org/multipage/grouping-content.html#the-div-element) 14 | 15 | So, semantically valid HTML must include both {'
    '} and {'
    '}
    . 16 | 17 | ### But why does it still work in browsers? 18 | 19 | This is where the HTML parser spec comes in — particularly the ["in body" insertion mode](https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody). This defines how HTML parsers should behave. 20 | 21 | From the spec: 22 | 23 | > An end tag whose tag name is one of: `div`, `section`, `article` ... 24 | > 25 | > **If there is no matching open element on the stack** 26 | > 27 | > It's a parse error and the token is ignored. 28 | > 29 | > **Otherwise:** 30 | > 31 | > Generate implied end tags. 32 | 33 | In short, the parser will generate implied end tags for any unclosed normal elements. 34 | 35 | ```html 36 |
    37 |
    38 |
    FullstacksJS
    39 |
    40 |
    41 |
    42 | ``` 43 | 44 | And the color of the text "FullstacksJS" is blue. 45 | -------------------------------------------------------------------------------- /src/app/review/_components/review-option.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type { ReactNode } from "react"; 4 | 5 | import { cn } from "@app/cn"; 6 | import { useUserAnswers } from "@app/state/useAnswers"; 7 | import { useIsClient } from "usehooks-ts"; 8 | 9 | import { RadioLabel } from "../../_components/radio-label"; 10 | 11 | interface Props { 12 | children: ReactNode; 13 | optionId: number; 14 | correctAnswerId: number; 15 | questionId: number; 16 | percentage: number; 17 | } 18 | 19 | export function ReviewOption({ 20 | children, 21 | optionId, 22 | correctAnswerId, 23 | questionId, 24 | percentage, 25 | }: Props) { 26 | const answers = useUserAnswers(); 27 | const isClient = useIsClient(); 28 | 29 | const userAnswer = answers[questionId]; 30 | 31 | const isUserAnswerCorrect = correctAnswerId === userAnswer; 32 | const isCorrectOption = optionId === correctAnswerId; 33 | const isWrongAnswered = 34 | isClient && !isUserAnswerCorrect && userAnswer === optionId; 35 | 36 | return ( 37 | 43 |
    51 | {children} 52 | {Math.round(percentage)}% 53 | 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/questions/question-8/index.tsx: -------------------------------------------------------------------------------- 1 | import { codeBlock } from "@app/mdx/code-block"; 2 | import { compileMDX } from "@app/mdx/compileMdx"; 3 | 4 | import type { Question } from "../Question"; 5 | 6 | import { IDontKnow } from "../shared-options"; 7 | import inquiry from "./inquiry.mdx"; 8 | 9 | export default { 10 | id: 8, 11 | inquiry, 12 | options: [ 13 | { 14 | id: 1, 15 | text: await compileMDX( 16 | codeBlock` 17 |

    18 | The event took place on 19 | June 15. 20 |

    `, 21 | ), 22 | }, 23 | { 24 | id: 2, 25 | text: await compileMDX( 26 | codeBlock` 27 |

    28 | The event took place on 29 | June 15. 30 |

    `, 31 | ), 32 | }, 33 | { 34 | id: 3, 35 | text: await compileMDX( 36 | codeBlock` 37 |

    38 | The event took place on 39 | June 15. 40 |

    `, 41 | ), 42 | }, 43 | { 44 | id: 4, 45 | text: await compileMDX( 46 | codeBlock` 47 |

    48 | The event took place on 49 | . 50 |

    `, 51 | ), 52 | }, 53 | IDontKnow, 54 | ], 55 | explanation: await compileMDX(` 56 | > The {' HTML element represents a specific period in time. 57 | > It may include the {"datetime"} attribute to translate dates into machine-readable format, allowing for better search engine results or custom features such as reminders. 58 | > [Reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time) 59 | `), 60 | correctAnswerId: 4, 61 | } satisfies Question; 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "you-dont-know-html", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "If you think you know HTML, think again.", 6 | "scripts": { 7 | "build": "next build --webpack", 8 | "dev": "next dev --webpack", 9 | "lint": "eslint", 10 | "lint:fix": "next lint --fix", 11 | "start": "next start", 12 | "supabase:gen": "./scripts/generate-supabase-types", 13 | "supabase:start": "supabase start", 14 | "supabase:stop": "supabase stop", 15 | "prepare": "./scripts/prepare" 16 | }, 17 | "dependencies": { 18 | "@fullstacksjs/config": "1.1.0", 19 | "@fullstacksjs/toolbox": "4.22.0", 20 | "@mdx-js/loader": "3.1.1", 21 | "@mdx-js/mdx": "3.1.1", 22 | "@mdx-js/react": "3.1.1", 23 | "@next/mdx": "16.0.10", 24 | "@radix-ui/react-slot": "1.2.4", 25 | "@shikijs/transformers": "3.20.0", 26 | "@supabase/ssr": "0.8.0", 27 | "@tailwindcss/postcss": "4.1.18", 28 | "@types/mdx": "2.0.13", 29 | "clsx": "2.1.1", 30 | "dedent": "1.7.1", 31 | "hast-util-to-jsx-runtime": "2.3.6", 32 | "next": "16.0.10", 33 | "postcss": "8.5.6", 34 | "posthog-js": "1.309.1", 35 | "posthog-node": "5.17.4", 36 | "react": "19.2.3", 37 | "react-aria": "3.45.0", 38 | "react-canvas-confetti": "2.0.7", 39 | "react-dom": "19.2.3", 40 | "server-only": "0.0.1", 41 | "tailwind-merge": "3.4.0", 42 | "tailwindcss": "4.1.18", 43 | "usehooks-ts": "3.1.1" 44 | }, 45 | "devDependencies": { 46 | "@fullstacksjs/eslint-config": "13.12.0", 47 | "@shikijs/rehype": "3.20.0", 48 | "@types/hast": "3.0.4", 49 | "@types/node": "24.10.2", 50 | "@types/react": "^19.2.7", 51 | "@types/react-dom": "^19.2.3", 52 | "cspell": "9.4.0", 53 | "eslint": "9.39.2", 54 | "husky": "9.1.7", 55 | "lint-staged": "16.2.7", 56 | "prettier": "3.7.4", 57 | "shiki": "3.20.0", 58 | "supabase": "2.67.2", 59 | "typescript": "5.9.3" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/app/quiz/_components/question/question.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useStoreAnswer, useUserAnswers } from "@app/state/useAnswers"; 4 | import { useRouter } from "next/navigation"; 5 | import { 6 | startTransition, 7 | useActionState, 8 | useEffect, 9 | ViewTransition, 10 | } from "react"; 11 | 12 | import { submitAnswer } from "../../_actions/submit-answer"; 13 | import { QuestionOption } from "./question-option"; 14 | 15 | interface Props { 16 | id: number; 17 | inquiry: React.ReactNode; 18 | step: number; 19 | options: { 20 | id: number; 21 | text: React.ReactNode; 22 | }[]; 23 | isLastQuestion: boolean; 24 | } 25 | 26 | export function Question({ 27 | id, 28 | inquiry, 29 | options, 30 | step, 31 | isLastQuestion, 32 | }: Props) { 33 | const [, formAction, pending] = useActionState(submitAnswer, {}); 34 | const setUserAnswers = useStoreAnswer(id); 35 | const userAnswers = useUserAnswers(); 36 | const router = useRouter(); 37 | 38 | useEffect(() => { 39 | window.scrollTo({ top: 0 }); 40 | }, [step]); 41 | 42 | const handleSelect = (optionId: number) => { 43 | setUserAnswers(optionId); 44 | 45 | if (!isLastQuestion) { 46 | router.push(`/quiz/${step + 1}`); 47 | return; 48 | } 49 | 50 | startTransition(() => { 51 | // Need to update the answers object manually to avoid the stale state 52 | const answers = { ...userAnswers }; 53 | answers[id] = optionId; 54 | formAction({ answers }); 55 | }); 56 | }; 57 | 58 | return ( 59 | 60 |
    61 |
    {inquiry}
    62 |
    63 |
      64 | {options.map((option, index) => ( 65 |
    • 66 | 74 | {option.text} 75 | 76 |
    • 77 | ))} 78 |
    79 |
    80 |
    81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /src/lib/mdx/white-space.ts: -------------------------------------------------------------------------------- 1 | import type { Element, Root, Text } from "hast"; 2 | import type { ShikiTransformer } from "shiki"; 3 | 4 | function findCode(root: Root): Element { 5 | let current = root.children.at(0); 6 | 7 | while (current?.type === "element") { 8 | if (current.tagName === "code") return current; 9 | current = current.children.at(0); 10 | 11 | if (!current) throw new Error("No code element found"); 12 | } 13 | 14 | throw new Error("No code element found"); 15 | } 16 | 17 | const classMap: Record = { 18 | " ": "space", 19 | "\t": "tab", 20 | }; 21 | 22 | function isSpace(part: string): boolean { 23 | return part === " " || part === "\t"; 24 | } 25 | 26 | export function splitSpaces(node: Text): string[] { 27 | const parts = node.value.split(/([\t ])/).filter((i) => i.length); 28 | 29 | const leftCount = Math.max( 30 | parts.findIndex((part) => !isSpace(part)), 31 | 0, 32 | ); 33 | 34 | const middle = parts.slice(leftCount, parts.length); 35 | if (leftCount > 0) return [" ", middle.join("")]; 36 | 37 | return [middle.join("")]; 38 | } 39 | 40 | export const renderWhitespaceTransformer: ShikiTransformer = { 41 | name: "transformers:white-space", 42 | 43 | root(root) { 44 | const code = findCode(root); 45 | code.children.forEach((line) => { 46 | if (line.type !== "element") return; 47 | const elements = line.children.filter( 48 | (token) => token.type === "element", 49 | ); 50 | line.children = line.children.flatMap((token) => { 51 | if (token.type !== "element") return token; 52 | 53 | const index = elements.indexOf(token); 54 | if (index !== 0) return token; 55 | 56 | const node = token.children.at(0); 57 | if (node?.type !== "text" || !node.value) return token; 58 | 59 | const parts = splitSpaces(node); 60 | if (parts.length <= 1) return token; 61 | 62 | return parts.map((part) => { 63 | const clone = { 64 | ...token, 65 | properties: { ...token.properties }, 66 | }; 67 | clone.children = [{ type: "text", value: part }]; 68 | const className = classMap[part]; 69 | 70 | if (className) { 71 | this.addClassToHast(clone, className); 72 | delete clone.properties.style; 73 | } 74 | 75 | return clone; 76 | }); 77 | }); 78 | }); 79 | }, 80 | }; 81 | -------------------------------------------------------------------------------- /src/lib/supabase/supabase.ts: -------------------------------------------------------------------------------- 1 | import { isNull } from "@fullstacksjs/toolbox"; 2 | import "server-only"; 3 | 4 | import type { Database } from "./Database"; 5 | 6 | import { createSupabaseClient } from "./createClient"; 7 | 8 | export interface AnswerDto { 9 | questionId: number; 10 | answerId: number; 11 | correct: boolean; 12 | } 13 | 14 | type CreateAnswerSchema = Database["public"]["Tables"]["answer"]["Insert"]; 15 | 16 | export async function createGame(answers: AnswerDto[]) { 17 | const client = await createSupabaseClient(); 18 | const { data: game, error: insertGameError } = await client 19 | .from("game") 20 | .insert({}) 21 | .select("id") 22 | .single(); 23 | 24 | if (!game) 25 | throw new Error("Failed to create game", { cause: insertGameError }); 26 | 27 | const { data, error: insertAnswersError } = await client 28 | .from("answer") 29 | .insert( 30 | answers.map((a) => ({ 31 | answer_id: a.answerId, 32 | question_id: a.questionId, 33 | is_correct: a.correct, 34 | game_id: game.id, 35 | })), 36 | ) 37 | .select("id"); 38 | 39 | if (!data) 40 | throw new Error("Failed to insert answers", { cause: insertAnswersError }); 41 | 42 | return game.id; 43 | } 44 | 45 | export async function getAnswersCount(questionId: number, optionId: number) { 46 | const client = await createSupabaseClient(); 47 | const { count, error } = await client 48 | .from("answer") 49 | .select("*", { count: "exact" }) 50 | .eq("question_id", questionId) 51 | .eq("answer_id", optionId); 52 | 53 | if (isNull(count)) 54 | throw new Error("Failed to get answers count", { cause: error }); 55 | 56 | return count; 57 | } 58 | 59 | export async function getGamesCount(questionId: number) { 60 | const client = await createSupabaseClient(); 61 | const { count, error } = await client 62 | .from("answer") 63 | .select("*", { count: "exact" }) 64 | .eq("question_id", questionId); 65 | 66 | if (isNull(count)) 67 | throw new Error("Failed to get games count", { cause: error }); 68 | 69 | return count; 70 | } 71 | 72 | export async function getGamesCountWithCorrectAnswers(correctAnswers: number) { 73 | const client = await createSupabaseClient(); 74 | const { data, error } = await client.rpc("get_games_with_correct_answers", { 75 | min_correct_answers: correctAnswers, 76 | }); 77 | 78 | if (error) throw new Error("Failed to get rank", { cause: error }); 79 | 80 | return data.length; 81 | } 82 | -------------------------------------------------------------------------------- /src/app/summary/_components/summary-result.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { isNull } from "@fullstacksjs/toolbox"; 4 | import { use, useEffect, useState } from "react"; 5 | import { useIsClient } from "usehooks-ts"; 6 | 7 | import { getMyPercentile } from "../_actions/getMyPercentile"; 8 | import { AnswersContext } from "./answers-provider"; 9 | import { Skeleton } from "./skeleton"; 10 | 11 | function getQuizResultMessage(score: number, total: number) { 12 | const percentage = (score * 100) / total; 13 | 14 | if (percentage === 0) { 15 | return ( 16 | <> 17 | 😅 You got 0 out of {total}. 18 |
    19 | At least you're honest! Better luck next time. 😉 20 | 21 | ); 22 | } else if (percentage === 100) { 23 | return ( 24 | <> 25 | 🔥 Flawless! You nailed all {total} questions. 26 |
    You really *know* HTML! 27 | 28 | ); 29 | } else if (percentage >= 75) { 30 | return ( 31 | <> 32 | 👍 You scored {score} out of {total}. 33 |
    34 | Excellent! You're really close to perfection. Keep up the great work! 35 | 36 | ); 37 | } else if (percentage >= 50) { 38 | return ( 39 | <> 40 | 👍 You scored {score} out of {total}. 41 |
    42 | Great job! You're on the right track — keep going, you're doing awesome! 43 | 44 | ); 45 | } else if (percentage >= 25) { 46 | return ( 47 | <> 48 | You scored {score} out of {total}. 49 |
    50 | Not bad! You’re getting there. Keep practicing and you'll improve fast! 51 | 52 | ); 53 | } else { 54 | return ( 55 | <> 56 | You scored {score} out of {total}. 57 |
    58 | Not bad! Keep sharpening those tags. You're on the right track! 59 | 60 | ); 61 | } 62 | } 63 | 64 | function getPercentileMessage(percentile: number) { 65 | if (percentile === 0) return "You're in a league of your own!"; 66 | return `You did better than ${percentile}% of people!`; 67 | } 68 | 69 | export function SummaryResult() { 70 | const { correctAnswers, loading, total } = use(AnswersContext); 71 | const isClient = useIsClient(); 72 | const [percentile, setPercentile] = useState(); 73 | const isPercentileLoading = !isClient || isNull(percentile); 74 | 75 | useEffect(() => { 76 | getMyPercentile(correctAnswers) 77 | .then(setPercentile) 78 | .catch(() => { 79 | setPercentile(0); 80 | }); 81 | }, [correctAnswers]); 82 | 83 | if (loading) return ; 84 | 85 | return ( 86 |
    87 |

    88 | {getQuizResultMessage(correctAnswers, total)} 89 |

    90 | {isPercentileLoading ? ( 91 | 92 | ) : ( 93 |

    94 | {getPercentileMessage(percentile)} 95 |

    96 | )} 97 |
    98 | ); 99 | } 100 | -------------------------------------------------------------------------------- /supabase/migrations/20250320080206_init.sql: -------------------------------------------------------------------------------- 1 | -- Table: public.game 2 | CREATE TABLE public.game ( 3 | id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY, 4 | created_at timestamp with time zone NOT NULL DEFAULT now(), 5 | updated_at timestamp with time zone NOT NULL DEFAULT now() 6 | ); 7 | 8 | -- Table: public.answer 9 | CREATE TABLE public.answer ( 10 | id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY, 11 | answer_id bigint, 12 | created_at timestamp with time zone NOT NULL DEFAULT now(), 13 | updated_at timestamp with time zone NOT NULL DEFAULT now(), 14 | game_id bigint, 15 | question_id bigint, 16 | is_correct boolean 17 | ); 18 | 19 | ALTER TABLE "public"."answer" ADD CONSTRAINT "answer_game_id_fkey" FOREIGN KEY (game_id) REFERENCES game(id) ON UPDATE CASCADE ON DELETE CASCADE not valid; 20 | ALTER TABLE "public"."answer" validate constraint "answer_game_id_fkey"; 21 | 22 | GRANT DELETE ON TABLE "public"."answer" TO "anon"; 23 | GRANT INSERT ON TABLE "public"."answer" TO "anon"; 24 | GRANT REFERENCES ON TABLE "public"."answer" TO "anon"; 25 | GRANT SELECT ON TABLE "public"."answer" TO "anon"; 26 | GRANT TRIGGER ON TABLE "public"."answer" TO "anon"; 27 | GRANT TRUNCATE ON TABLE "public"."answer" TO "anon"; 28 | GRANT UPDATE ON TABLE "public"."answer" TO "anon"; 29 | 30 | GRANT DELETE ON TABLE "public"."answer" TO "authenticated"; 31 | GRANT INSERT ON TABLE "public"."answer" TO "authenticated"; 32 | GRANT REFERENCES ON TABLE "public"."answer" TO "authenticated"; 33 | GRANT SELECT ON TABLE "public"."answer" TO "authenticated"; 34 | GRANT TRIGGER ON TABLE "public"."answer" TO "authenticated"; 35 | GRANT TRUNCATE ON TABLE "public"."answer" TO "authenticated"; 36 | GRANT UPDATE ON TABLE "public"."answer" TO "authenticated"; 37 | 38 | GRANT DELETE ON TABLE "public"."answer" TO "service_role"; 39 | GRANT INSERT ON TABLE "public"."answer" TO "service_role"; 40 | GRANT REFERENCES ON TABLE "public"."answer" TO "service_role"; 41 | GRANT SELECT ON TABLE "public"."answer" TO "service_role"; 42 | GRANT TRIGGER ON TABLE "public"."answer" TO "service_role"; 43 | GRANT TRUNCATE ON TABLE "public"."answer" TO "service_role"; 44 | GRANT UPDATE ON TABLE "public"."answer" TO "service_role"; 45 | 46 | GRANT DELETE ON TABLE "public"."game" TO "anon"; 47 | GRANT INSERT ON TABLE "public"."game" TO "anon"; 48 | GRANT REFERENCES ON TABLE "public"."game" TO "anon"; 49 | GRANT SELECT ON TABLE "public"."game" TO "anon"; 50 | GRANT TRIGGER ON TABLE "public"."game" TO "anon"; 51 | GRANT TRUNCATE ON TABLE "public"."game" TO "anon"; 52 | GRANT UPDATE ON TABLE "public"."game" TO "anon"; 53 | 54 | GRANT DELETE ON TABLE "public"."game" TO "authenticated"; 55 | GRANT INSERT ON TABLE "public"."game" TO "authenticated"; 56 | GRANT REFERENCES ON TABLE "public"."game" TO "authenticated"; 57 | GRANT SELECT ON TABLE "public"."game" TO "authenticated"; 58 | GRANT TRIGGER ON TABLE "public"."game" TO "authenticated"; 59 | GRANT TRUNCATE ON TABLE "public"."game" TO "authenticated"; 60 | GRANT UPDATE ON TABLE "public"."game" TO "authenticated"; 61 | 62 | GRANT DELETE ON TABLE "public"."game" TO "service_role"; 63 | GRANT INSERT ON TABLE "public"."game" TO "service_role"; 64 | GRANT REFERENCES ON TABLE "public"."game" TO "service_role"; 65 | GRANT SELECT ON TABLE "public"."game" TO "service_role"; 66 | GRANT TRIGGER ON TABLE "public"."game" TO "service_role"; 67 | GRANT TRUNCATE ON TABLE "public"."game" TO "service_role"; 68 | GRANT UPDATE ON TABLE "public"."game" TO "service_role"; 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/lib/supabase/Database.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export type Json = 3 | | string 4 | | number 5 | | boolean 6 | | null 7 | | { [key: string]: Json | undefined } 8 | | Json[] 9 | 10 | export type Database = { 11 | graphql_public: { 12 | Tables: { 13 | [_ in never]: never 14 | } 15 | Views: { 16 | [_ in never]: never 17 | } 18 | Functions: { 19 | graphql: { 20 | Args: { 21 | operationName?: string 22 | query?: string 23 | variables?: Json 24 | extensions?: Json 25 | } 26 | Returns: Json 27 | } 28 | } 29 | Enums: { 30 | [_ in never]: never 31 | } 32 | CompositeTypes: { 33 | [_ in never]: never 34 | } 35 | } 36 | public: { 37 | Tables: { 38 | answer: { 39 | Row: { 40 | answer_id: number | null 41 | created_at: string 42 | game_id: number | null 43 | id: number 44 | is_correct: boolean | null 45 | question_id: number | null 46 | updated_at: string 47 | } 48 | Insert: { 49 | answer_id?: number | null 50 | created_at?: string 51 | game_id?: number | null 52 | id?: never 53 | is_correct?: boolean | null 54 | question_id?: number | null 55 | updated_at?: string 56 | } 57 | Update: { 58 | answer_id?: number | null 59 | created_at?: string 60 | game_id?: number | null 61 | id?: never 62 | is_correct?: boolean | null 63 | question_id?: number | null 64 | updated_at?: string 65 | } 66 | Relationships: [ 67 | { 68 | foreignKeyName: "answer_game_id_fkey" 69 | columns: ["game_id"] 70 | isOneToOne: false 71 | referencedRelation: "game" 72 | referencedColumns: ["id"] 73 | }, 74 | ] 75 | } 76 | game: { 77 | Row: { 78 | created_at: string 79 | id: number 80 | updated_at: string 81 | } 82 | Insert: { 83 | created_at?: string 84 | id?: never 85 | updated_at?: string 86 | } 87 | Update: { 88 | created_at?: string 89 | id?: never 90 | updated_at?: string 91 | } 92 | Relationships: [] 93 | } 94 | } 95 | Views: { 96 | [_ in never]: never 97 | } 98 | Functions: { 99 | get_games_with_correct_answers: { 100 | Args: { 101 | min_correct_answers: number 102 | } 103 | Returns: { 104 | game_id: number 105 | correct_answers_count: number 106 | }[] 107 | } 108 | } 109 | Enums: { 110 | [_ in never]: never 111 | } 112 | CompositeTypes: { 113 | [_ in never]: never 114 | } 115 | } 116 | } 117 | 118 | type DefaultSchema = Database[Extract] 119 | 120 | export type Tables< 121 | DefaultSchemaTableNameOrOptions extends 122 | | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) 123 | | { schema: keyof Database }, 124 | TableName extends DefaultSchemaTableNameOrOptions extends { 125 | schema: keyof Database 126 | } 127 | ? keyof (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & 128 | Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) 129 | : never = never, 130 | > = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } 131 | ? (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & 132 | Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { 133 | Row: infer R 134 | } 135 | ? R 136 | : never 137 | : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & 138 | DefaultSchema["Views"]) 139 | ? (DefaultSchema["Tables"] & 140 | DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { 141 | Row: infer R 142 | } 143 | ? R 144 | : never 145 | : never 146 | 147 | export type TablesInsert< 148 | DefaultSchemaTableNameOrOptions extends 149 | | keyof DefaultSchema["Tables"] 150 | | { schema: keyof Database }, 151 | TableName extends DefaultSchemaTableNameOrOptions extends { 152 | schema: keyof Database 153 | } 154 | ? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] 155 | : never = never, 156 | > = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } 157 | ? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { 158 | Insert: infer I 159 | } 160 | ? I 161 | : never 162 | : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] 163 | ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { 164 | Insert: infer I 165 | } 166 | ? I 167 | : never 168 | : never 169 | 170 | export type TablesUpdate< 171 | DefaultSchemaTableNameOrOptions extends 172 | | keyof DefaultSchema["Tables"] 173 | | { schema: keyof Database }, 174 | TableName extends DefaultSchemaTableNameOrOptions extends { 175 | schema: keyof Database 176 | } 177 | ? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] 178 | : never = never, 179 | > = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } 180 | ? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { 181 | Update: infer U 182 | } 183 | ? U 184 | : never 185 | : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] 186 | ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { 187 | Update: infer U 188 | } 189 | ? U 190 | : never 191 | : never 192 | 193 | export type Enums< 194 | DefaultSchemaEnumNameOrOptions extends 195 | | keyof DefaultSchema["Enums"] 196 | | { schema: keyof Database }, 197 | EnumName extends DefaultSchemaEnumNameOrOptions extends { 198 | schema: keyof Database 199 | } 200 | ? keyof Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] 201 | : never = never, 202 | > = DefaultSchemaEnumNameOrOptions extends { schema: keyof Database } 203 | ? Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] 204 | : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] 205 | ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] 206 | : never 207 | 208 | export type CompositeTypes< 209 | PublicCompositeTypeNameOrOptions extends 210 | | keyof DefaultSchema["CompositeTypes"] 211 | | { schema: keyof Database }, 212 | CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { 213 | schema: keyof Database 214 | } 215 | ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] 216 | : never = never, 217 | > = PublicCompositeTypeNameOrOptions extends { schema: keyof Database } 218 | ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] 219 | : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] 220 | ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] 221 | : never 222 | 223 | export const Constants = { 224 | graphql_public: { 225 | Enums: {}, 226 | }, 227 | public: { 228 | Enums: {}, 229 | }, 230 | } as const 231 | -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | font-size: clamp(0.9rem, .8rem + 0.2vw, 1.4rem); 5 | --sb-track-color: var(--color-shade); 6 | --sb-thumb-color: var(--color-shade-2); 7 | --font-mono: var(--font-fira-mono); 8 | --font-size-question: 1.2rem; 9 | scroll-behavior: smooth; 10 | } 11 | 12 | @media (width >= 48rem) { 13 | :root { 14 | --font-size-question: 1.7rem; 15 | } 16 | } 17 | 18 | @theme { 19 | --color-*: initial; 20 | --color-background: #23252e; 21 | --color-bg-elevated: #282b37; 22 | --color-foreground: #e6e6e6; 23 | --color-accent: #f39f47; 24 | --color-muted-1: #ffffff66; 25 | --color-muted-2: #585858; 26 | --color-success: #00a16e; 27 | --color-info: #a96fff; 28 | --color-error: #ca343e; 29 | --color-shade: rgb(0 0 0 / 0.1); 30 | --color-shade-2: rgb(0 0 0 / 0.2); 31 | --color-shade-4: rgb(0 0 0 / 0.4); 32 | --color-border-dark: rgb(0 0 0 / 0.4); 33 | 34 | --spacing: 0.25rem; 35 | 36 | --text-hero: 2.5rem; 37 | --text-hero--line-height: 1.3; 38 | --text-hero--font-weight: 700; 39 | 40 | --text-question: var(--font-size-question); 41 | --text-question--line-height: 1.2; 42 | --text-question--font-weight: 700; 43 | 44 | --rounded-md: 4px; 45 | 46 | --animate-loading: loading 1s linear alternate infinite; 47 | 48 | --shadow-radio-ring: 0 0 0 4px var(--color-background); 49 | } 50 | 51 | [data-index="0"] { 52 | animation-delay: 0s; 53 | } 54 | 55 | [data-index="1"] { 56 | animation-delay: 0.15s; 57 | } 58 | 59 | [data-index="2"] { 60 | animation-delay: 0.3s; 61 | } 62 | 63 | [data-index="3"] { 64 | animation-delay: 0.45s; 65 | } 66 | 67 | [data-index="4"] { 68 | animation-delay: 0.6s; 69 | } 70 | 71 | [data-index="5"] { 72 | animation-delay: 0.75s; 73 | } 74 | 75 | [data-index="6"] { 76 | animation-delay: 0.9s; 77 | } 78 | 79 | @keyframes loading { 80 | from { 81 | opacity: .3; 82 | } 83 | to { 84 | opacity: 1; 85 | } 86 | } 87 | 88 | @utility area-* { 89 | grid-area: --value([ *]); 90 | } 91 | 92 | .welcome-main { 93 | grid-template: 94 | "." 1fr 95 | "hero" auto 96 | "." 1fr 97 | "btn" auto; 98 | 99 | @media (width >= 48rem) { 100 | place-content: center; 101 | grid-template: 102 | "hero" auto 103 | "btn" auto / 400px; 104 | } 105 | } 106 | 107 | ::-webkit-scrollbar { 108 | height: 8px; 109 | } 110 | 111 | ::-webkit-scrollbar-track { 112 | background: var(--sb-track-color); 113 | border-radius: 2px; 114 | } 115 | 116 | ::-webkit-scrollbar-thumb { 117 | background: var(--sb-thumb-color); 118 | border-radius: 2px; 119 | } 120 | 121 | @supports not selector(::-webkit-scrollbar) { 122 | body { 123 | scrollbar-color: var(--sb-thumb-color) var(--sb-track-color); 124 | } 125 | } 126 | 127 | .question { 128 | display: flex; 129 | flex-direction: column; 130 | gap: calc(var(--spacing) * 4); 131 | margin-bottom: calc(var(--spacing) * 4); 132 | 133 | [data-code-block-container] { 134 | background-color: var(--color-shade); 135 | border: 1px solid var(--color-shade-2); 136 | border-radius: var(--rounded-md); 137 | padding: 0.4rem; 138 | [data-syntax-highlight] { 139 | overflow-x: auto; 140 | } 141 | 142 | @media (width >= 48rem) { 143 | padding: 0.8rem 1rem; 144 | } 145 | } 146 | } 147 | 148 | [data-code-block-container] { 149 | width: 100%; 150 | display: block; 151 | } 152 | 153 | [data-syntax-highlight] { 154 | code { 155 | font-family: var(--font-mono); 156 | counter-reset: step; 157 | counter-increment: step 0; 158 | line-height: 2; 159 | font-weight: 500; 160 | width: 100%; 161 | display: block; 162 | 163 | font-size: var(--text-sm); 164 | @media (width >= 48rem) { 165 | font-size: 1.1rem; 166 | } 167 | 168 | .line::before { 169 | content: counter(step); 170 | counter-increment: step; 171 | width: 1rem; 172 | margin-right: 1rem; 173 | display: inline-block; 174 | text-align: right; 175 | color: rgba(115, 138, 148, 0.4); 176 | } 177 | 178 | .space::after { 179 | content: "''"; 180 | --un-icon: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 16 16' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M4 8a.5.5 0 0 1 .5-.5h5.793L8.146 5.354a.5.5 0 1 1 .708-.708l3 3a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708-.708L10.293 8.5H4.5A.5.5 0 0 1 4 8'/%3E%3C/svg%3E"); 181 | 182 | -webkit-mask: var(--un-icon) no-repeat; 183 | mask: var(--un-icon) no-repeat; 184 | -webkit-mask-size: 100% 100%; 185 | mask-size: 100% 100%; 186 | background-color: currentColor; 187 | display: inline-block; 188 | color: var(--color-muted-2); 189 | translate: -30% 0%; 190 | } 191 | } 192 | } 193 | 194 | :not([data-syntax-highlight]) > [data-inline-code] { 195 | background: var(--color-shade-2); 196 | padding: 4px 8px; 197 | font-size: 0.8em; 198 | border-radius: 8px; 199 | display: inline-block; 200 | } 201 | 202 | label [data-code-block-container] { 203 | overflow-x: auto; 204 | } 205 | 206 | ::view-transition-group(question) { 207 | animation-duration: 10ms; 208 | display: none; 209 | } 210 | 211 | @media (prefers-reduced-motion: no-preference) { 212 | ::view-transition-group(question) { 213 | animation-duration: 200ms; 214 | display: block; 215 | } 216 | 217 | ::view-transition-old(question) { 218 | animation-name: slide-out; 219 | } 220 | 221 | ::view-transition-new(question) { 222 | animation-name: slide-in; 223 | } 224 | } 225 | 226 | @keyframes fade-in { 227 | from { 228 | opacity: 0; 229 | } 230 | to { 231 | opacity: 1; 232 | } 233 | } 234 | 235 | @keyframes fade-out { 236 | from { 237 | opacity: 1; 238 | } 239 | to { 240 | opacity: 0; 241 | } 242 | } 243 | 244 | @keyframes slide-in { 245 | from { 246 | translate: 0 100vh; 247 | } 248 | to { 249 | translate: 0; 250 | } 251 | } 252 | 253 | @keyframes slide-out { 254 | from { 255 | translate: 0; 256 | } 257 | to { 258 | translate: 0 100vh; 259 | } 260 | } 261 | 262 | .i-arrow-left { 263 | --un-icon: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m10 18l-6-6l6-6l1.4 1.45L7.85 11H20v2H7.85l3.55 3.55z'/%3E%3C/svg%3E"); 264 | -webkit-mask: var(--un-icon) no-repeat; 265 | mask: var(--un-icon) no-repeat; 266 | -webkit-mask-size: 100% 100%; 267 | mask-size: 100% 100%; 268 | background-color: currentColor; 269 | color: inherit; 270 | width: 1.2em; 271 | height: 1.2em; 272 | } 273 | 274 | .i-keyboard { 275 | --un-icon: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M3 20q-.825 0-1.412-.587T1 18V6q0-.825.588-1.412T3 4h18q.825 0 1.413.588T23 6v12q0 .825-.587 1.413T21 20zm5-3h8v-2H8zm-3-3.5h2v-2H5zm4 0h2v-2H9zm4 0h2v-2h-2zm4 0h2v-2h-2zM5 10h2V8H5zm4 0h2V8H9zm4 0h2V8h-2zm4 0h2V8h-2z'/%3E%3C/svg%3E"); 276 | -webkit-mask: var(--un-icon) no-repeat; 277 | mask: var(--un-icon) no-repeat; 278 | -webkit-mask-size: 100% 100%; 279 | mask-size: 100% 100%; 280 | background-color: currentColor; 281 | color: inherit; 282 | width: 1em; 283 | height: 1em; 284 | vertical-align: text-bottom; 285 | display: inline-block; 286 | } 287 | 288 | a[data-external]::after { 289 | --un-icon: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 48 48' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='4' d='m16 32l17-17m-18 0h18v18'/%3E%3C/svg%3E"); 290 | content: '""'; 291 | -webkit-mask: var(--un-icon) no-repeat; 292 | mask: var(--un-icon) no-repeat; 293 | -webkit-mask-size: 100% 100%; 294 | mask-size: 100% 100%; 295 | background-color: currentColor; 296 | margin-left: 0.1em; 297 | color: inherit; 298 | display: inline; 299 | } 300 | 301 | .i-x { 302 | --un-icon: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 16 16' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M.102 0h4.71l4.193 5.997L14.04 0h1.505L9.58 6.82L16 16h-4.71L6.911 9.738L1.504 16H0l6.337-7.083zM1.96.941l9.872 14.118h2.31L4.27.94z'/%3E%3C/svg%3E"); 303 | -webkit-mask: var(--un-icon) no-repeat; 304 | mask: var(--un-icon) no-repeat; 305 | -webkit-mask-size: 100% 100%; 306 | mask-size: 100% 100%; 307 | background-color: currentColor; 308 | color: inherit; 309 | display: inline-block; 310 | width: 0.8em; 311 | height: 0.8em; 312 | } 313 | -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 13 | 14 | 17 | 18 | 19 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 52 | 53 | 54 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 71 | 72 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | 82 | 83 | 84 | 85 | 86 | 87 | 89 | 90 | 91 | 93 | 94 | 95 | 96 | 97 | 98 | 100 | 101 | 102 | 104 | 105 | 106 | 107 | 108 | 109 | 111 | 112 | 113 | 115 | 116 | 117 | 118 | 119 | 120 | 122 | 123 | 124 | 126 | 127 | 128 | 129 | 130 | 131 | 133 | 134 | 135 | 137 | 138 | 139 | 140 | 141 | 142 | 144 | 145 | 147 | 148 | 150 | 151 | 152 | 153 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /src/app/icon.tsx: -------------------------------------------------------------------------------- 1 | import { ImageResponse } from "next/og"; 2 | 3 | export const size = { 4 | width: 32, 5 | height: 32, 6 | }; 7 | export const contentType = "image/png"; 8 | 9 | // eslint-disable-next-line max-lines-per-function 10 | export default function Favicon() { 11 | return new ImageResponse( 12 | 19 | 25 | 31 | 35 | 36 | 40 | 41 | 42 | 48 | 49 | 50 | 54 | 55 | 56 | 57 | 58 | 59 | 63 | 64 | 65 | 69 | 70 | 71 | 75 | 76 | 77 | 81 | 82 | 83 | 89 | 90 | 91 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | 112 | 121 | 122 | 123 | 128 | 129 | 130 | 131 | 132 | 133 | 142 | 143 | 144 | 149 | 150 | 151 | 152 | 153 | 154 | 163 | 164 | 165 | 170 | 171 | 172 | 173 | 174 | 175 | 184 | 185 | 186 | 191 | 192 | 193 | 194 | 195 | 196 | 205 | 206 | 207 | 212 | 213 | 214 | 215 | 216 | 217 | 226 | 227 | 228 | 233 | 234 | 235 | 236 | 237 | 238 | 247 | 248 | 249 | 254 | 255 | 256 | 257 | 258 | 259 | 268 | 269 | 274 | 275 | 276 | 280 | 285 | 286 | 294 | 295 | 296 | 297 | 298 | , 299 | size, 300 | ); 301 | } 302 | -------------------------------------------------------------------------------- /src/lib/mdx/fullstacksjs-theme.ts: -------------------------------------------------------------------------------- 1 | import type { ThemeRegistrationAny } from "shiki"; 2 | 3 | /* cspell:disable */ 4 | export const fullstacksJSTheme: ThemeRegistrationAny = { 5 | name: "S-Kill", 6 | type: "dark", 7 | semanticHighlighting: true, 8 | tokenColors: [ 9 | { 10 | name: "unison punctuation", 11 | scope: 12 | "punctuation.definition.delayed.unison,punctuation.definition.list.begin.unison,punctuation.definition.list.end.unison,punctuation.definition.ability.begin.unison,punctuation.definition.ability.end.unison,punctuation.operator.assignment.as.unison,punctuation.separator.pipe.unison,punctuation.separator.delimiter.unison,punctuation.definition.hash.unison", 13 | settings: { foreground: "#ffffff" }, 14 | }, 15 | { 16 | name: "haskell variable generic-type", 17 | scope: "variable.other.generic-type.haskell", 18 | settings: { foreground: "#ff8888" }, 19 | }, 20 | { 21 | name: "haskell storage type", 22 | scope: "storage.type.haskell", 23 | settings: { foreground: "#14ffde" }, 24 | }, 25 | { 26 | name: "support.variable.magic.python", 27 | scope: "support.variable.magic.python", 28 | settings: { foreground: "#ffffff" }, 29 | }, 30 | { 31 | name: "punctuation.separator.parameters.python", 32 | scope: 33 | "punctuation.separator.period.python,punctuation.separator.element.python,punctuation.parenthesis.begin.python,punctuation.parenthesis.end.python", 34 | settings: { foreground: "#ffffff" }, 35 | }, 36 | { 37 | name: "variable.parameter.function.language.special.self.python", 38 | scope: "variable.parameter.function.language.special.self.python", 39 | settings: { foreground: "#ffffff" }, 40 | }, 41 | { 42 | name: "storage.modifier.lifetime.rust", 43 | scope: "storage.modifier.lifetime.rust", 44 | settings: { foreground: "#ffffff" }, 45 | }, 46 | { 47 | name: "support.function.std.rust", 48 | scope: "support.function.std.rust", 49 | settings: { foreground: "#ffa478" }, 50 | }, 51 | { 52 | name: "entity.name.lifetime.rust", 53 | scope: "entity.name.lifetime.rust", 54 | settings: { foreground: "#ffffff" }, 55 | }, 56 | { 57 | name: "variable.language.rust", 58 | scope: "variable.language.rust", 59 | settings: { foreground: "#ffffff" }, 60 | }, 61 | { 62 | name: "support.constant.edge", 63 | scope: "support.constant.edge", 64 | settings: { foreground: "#ff8888" }, 65 | }, 66 | { 67 | name: "regexp constant character-class", 68 | scope: "constant.other.character-class.regexp", 69 | settings: { foreground: "#ffffff" }, 70 | }, 71 | { 72 | name: "Text", 73 | scope: "variable.parameter.function, text.html.derivative", 74 | settings: { foreground: "#ffffff" }, 75 | }, 76 | { 77 | name: "Comment Markup Link", 78 | scope: "comment markup.link", 79 | settings: { foreground: "#ffffff44" }, 80 | }, 81 | { 82 | name: "markup diff", 83 | scope: "markup.changed.diff", 84 | settings: { foreground: "#ffffff" }, 85 | }, 86 | { 87 | name: "diff", 88 | scope: 89 | "meta.diff.header.from-file,meta.diff.header.to-file,punctuation.definition.from-file.diff,punctuation.definition.to-file.diff", 90 | settings: { foreground: "#ffa478" }, 91 | }, 92 | { 93 | name: "inserted.diff", 94 | scope: "markup.inserted.diff", 95 | settings: { foreground: "#83d6ff" }, 96 | }, 97 | { 98 | name: "deleted.diff", 99 | scope: "markup.deleted.diff", 100 | settings: { foreground: "#ffffff" }, 101 | }, 102 | { 103 | name: "c++ function", 104 | scope: "meta.function.c,meta.function.cpp", 105 | settings: { foreground: "#ffffff" }, 106 | }, 107 | { 108 | name: "c++ block", 109 | scope: 110 | "punctuation.section.block.begin.bracket.curly.cpp,punctuation.section.block.end.bracket.curly.cpp,punctuation.terminator.statement.c,punctuation.section.block.begin.bracket.curly.c,punctuation.section.block.end.bracket.curly.c,punctuation.section.parens.begin.bracket.round.c,punctuation.section.parens.end.bracket.round.c,punctuation.section.parameters.begin.bracket.round.c,punctuation.section.parameters.end.bracket.round.c", 111 | settings: { foreground: "#ffffff" }, 112 | }, 113 | { 114 | name: "js/ts punctuation separator key-value", 115 | scope: "punctuation.separator.key-value", 116 | settings: { foreground: "#ffffff" }, 117 | }, 118 | { 119 | name: "js/ts import keyword", 120 | scope: "keyword.operator.expression.import", 121 | settings: { foreground: "#ffa478" }, 122 | }, 123 | { 124 | name: "math js/ts", 125 | scope: "support.constant.math", 126 | settings: { foreground: "#ffffff" }, 127 | }, 128 | { 129 | name: "math property js/ts", 130 | scope: "support.constant.property.math", 131 | settings: { foreground: "#14ffde" }, 132 | }, 133 | { 134 | name: "js/ts variable.other.constant", 135 | scope: 136 | "variable.other.constant, variable.other.constant.tsx, variable.other.object", 137 | settings: { foreground: "#ffffff" }, 138 | }, 139 | { 140 | name: "java type", 141 | scope: ["storage.type.annotation.java", "storage.type.object.array.java"], 142 | settings: { foreground: "#ffffff" }, 143 | }, 144 | { 145 | name: "java source", 146 | scope: "source.java", 147 | settings: { foreground: "#ffffff" }, 148 | }, 149 | { 150 | name: "java modifier.import", 151 | scope: 152 | "punctuation.section.block.begin.java,punctuation.section.block.end.java,punctuation.definition.method-parameters.begin.java,punctuation.definition.method-parameters.end.java,meta.method.identifier.java,punctuation.section.method.begin.java,punctuation.section.method.end.java,punctuation.terminator.java,punctuation.section.class.begin.java,punctuation.section.class.end.java,punctuation.section.inner-class.begin.java,punctuation.section.inner-class.end.java,meta.method-call.java,punctuation.section.class.begin.bracket.curly.java,punctuation.section.class.end.bracket.curly.java,punctuation.section.method.begin.bracket.curly.java,punctuation.section.method.end.bracket.curly.java,punctuation.separator.period.java,punctuation.bracket.angle.java,punctuation.definition.annotation.java,meta.method.body.java", 153 | settings: { foreground: "#ffffff" }, 154 | }, 155 | { 156 | name: "java modifier.import", 157 | scope: "meta.method.java", 158 | settings: { foreground: "#ffa478" }, 159 | }, 160 | { 161 | name: "java modifier.import", 162 | scope: 163 | "storage.modifier.import.java,storage.type.java,storage.type.generic.java", 164 | settings: { foreground: "#ffffff" }, 165 | }, 166 | { 167 | name: "java instanceof", 168 | scope: "keyword.operator.instanceof.java", 169 | settings: { foreground: "#ff8888" }, 170 | }, 171 | { 172 | name: "java variable.name", 173 | scope: "meta.definition.variable.name.java", 174 | settings: { foreground: "#ffffff" }, 175 | }, 176 | { 177 | name: "operator logical", 178 | scope: "keyword.operator.logical", 179 | settings: { foreground: "#ffffff" }, 180 | }, 181 | { 182 | name: "operator bitwise", 183 | scope: "keyword.operator.bitwise", 184 | settings: { foreground: "#ffffff" }, 185 | }, 186 | { 187 | name: "operator channel", 188 | scope: "keyword.operator.channel", 189 | settings: { foreground: "#ffffff" }, 190 | }, 191 | { 192 | name: "support.constant.property-value.scss", 193 | scope: 194 | "support.constant.property-value.scss,support.constant.property-value.css", 195 | settings: { foreground: "#14ffde" }, 196 | }, 197 | { 198 | name: "CSS/SCSS/LESS Operators", 199 | scope: "keyword.operator.css,keyword.operator.scss,keyword.operator.less", 200 | settings: { foreground: "#ffffff" }, 201 | }, 202 | { 203 | name: "css color standard name", 204 | scope: 205 | "support.constant.color.w3c-standard-color-name.css,support.constant.color.w3c-standard-color-name.scss", 206 | settings: { foreground: "#14ffde" }, 207 | }, 208 | { 209 | name: "css comma", 210 | scope: "punctuation.separator.list.comma.css", 211 | settings: { foreground: "#ffffff" }, 212 | }, 213 | { 214 | name: "css attribute-name.id", 215 | scope: "support.constant.color.w3c-standard-color-name.css", 216 | settings: { foreground: "#14ffde" }, 217 | }, 218 | { 219 | name: "css property-name", 220 | scope: "support.type.vendored.property-name.css", 221 | settings: { foreground: "#ffffff" }, 222 | }, 223 | { 224 | name: "js/ts module", 225 | scope: 226 | "support.module.node,support.type.object.module,support.module.node", 227 | settings: { foreground: "#ffffff" }, 228 | }, 229 | { 230 | name: "Type Name", 231 | scope: "entity.name.type", 232 | settings: { foreground: "#14ffde" }, 233 | }, 234 | { 235 | name: "js variable readwrite", 236 | scope: 237 | "variable.other.readwrite,meta.object-literal.key,support.variable.property,support.variable.object.process,support.variable.object.node", 238 | settings: { foreground: "#ffffff" }, 239 | }, 240 | { 241 | name: "js/ts json", 242 | scope: "support.constant.json", 243 | settings: { foreground: "#14ffde" }, 244 | }, 245 | { 246 | name: "js/ts Keyword", 247 | scope: [ 248 | "keyword.operator.expression.instanceof", 249 | "keyword.operator.new", 250 | "keyword.operator.expression.keyof", 251 | ], 252 | settings: { foreground: "#ff8888" }, 253 | }, 254 | { 255 | name: "js/ts console", 256 | scope: "support.type.object.console", 257 | settings: { foreground: "#ffffff" }, 258 | }, 259 | { 260 | name: "js/ts support.variable.property.process", 261 | scope: "support.variable.property.process", 262 | settings: { foreground: "#14ffde" }, 263 | }, 264 | { 265 | name: "keyword.operator.misc.rust", 266 | scope: "keyword.operator.misc.rust", 267 | settings: { foreground: "#ffffff" }, 268 | }, 269 | { 270 | name: "keyword.operator.sigil.rust", 271 | scope: "keyword.operator.sigil.rust", 272 | settings: { foreground: "#ff8888" }, 273 | }, 274 | { 275 | name: "operator", 276 | scope: "keyword.operator.delete", 277 | settings: { foreground: "#ff8888" }, 278 | }, 279 | { 280 | name: "js dom", 281 | scope: "support.type.object.dom", 282 | settings: { foreground: "#ffffff" }, 283 | }, 284 | { 285 | name: "js dom variable", 286 | scope: "support.variable.dom,support.variable.property.dom", 287 | settings: { foreground: "#ffffff" }, 288 | }, 289 | { 290 | name: "keyword.operator", 291 | scope: 292 | "keyword.operator.arithmetic,keyword.operator.comparison,keyword.operator.decrement,keyword.operator.increment,keyword.operator.relational", 293 | settings: { foreground: "#ffffff" }, 294 | }, 295 | { 296 | name: "C operator assignment", 297 | scope: 298 | "keyword.operator.assignment.c,keyword.operator.comparison.c,keyword.operator.c,keyword.operator.increment.c,keyword.operator.decrement.c,keyword.operator.bitwise.shift.c,keyword.operator.assignment.cpp,keyword.operator.comparison.cpp,keyword.operator.cpp,keyword.operator.increment.cpp,keyword.operator.decrement.cpp,keyword.operator.bitwise.shift.cpp", 299 | settings: { foreground: "#ff8888" }, 300 | }, 301 | { 302 | name: "Punctuation", 303 | scope: "punctuation.separator.delimiter", 304 | settings: { foreground: "#ffffff" }, 305 | }, 306 | { 307 | name: "Other punctuation .c", 308 | scope: "punctuation.separator.c,punctuation.separator.cpp", 309 | settings: { foreground: "#ff8888" }, 310 | }, 311 | { 312 | name: "C type posix-reserved", 313 | scope: "support.type.posix-reserved.c,support.type.posix-reserved.cpp", 314 | settings: { foreground: "#ffffff" }, 315 | }, 316 | { 317 | name: "keyword.operator.sizeof.c", 318 | scope: "keyword.operator.sizeof.c,keyword.operator.sizeof.cpp", 319 | settings: { foreground: "#ff8888" }, 320 | }, 321 | { 322 | name: "python parameter", 323 | scope: "variable.parameter.function.language.python", 324 | settings: { foreground: "#14ffde" }, 325 | }, 326 | { 327 | name: "python type", 328 | scope: "support.type.python", 329 | settings: { foreground: "#ffffff" }, 330 | }, 331 | { 332 | name: "python logical", 333 | scope: "keyword.operator.logical.python", 334 | settings: { foreground: "#ff8888" }, 335 | }, 336 | { 337 | name: "pyCs", 338 | scope: "variable.parameter.function.python", 339 | settings: { foreground: "#14ffde" }, 340 | }, 341 | { 342 | name: "python block", 343 | scope: 344 | "punctuation.definition.arguments.begin.python,punctuation.definition.arguments.end.python,punctuation.separator.arguments.python,punctuation.definition.list.begin.python,punctuation.definition.list.end.python", 345 | settings: { foreground: "#ffffff" }, 346 | }, 347 | { 348 | name: "python function-call.generic", 349 | scope: "meta.function-call.generic.python", 350 | settings: { foreground: "#ffa478" }, 351 | }, 352 | { 353 | name: "python placeholder reset to normal string", 354 | scope: "constant.character.format.placeholder.other.python", 355 | settings: { foreground: "#14ffde" }, 356 | }, 357 | { 358 | name: "Operators", 359 | scope: "keyword.operator", 360 | settings: { foreground: "#ffffff" }, 361 | }, 362 | { 363 | name: "Compound Assignment Operators", 364 | scope: "keyword.operator.assignment.compound", 365 | settings: { foreground: "#ff8888" }, 366 | }, 367 | { 368 | name: "Compound Assignment Operators js/ts", 369 | scope: 370 | "keyword.operator.assignment.compound.js,keyword.operator.assignment.compound.ts", 371 | settings: { foreground: "#ffffff" }, 372 | }, 373 | { name: "Keywords", scope: "keyword", settings: { foreground: "#ff8888" } }, 374 | { 375 | name: "Namespaces", 376 | scope: "entity.name.namespace", 377 | settings: { foreground: "#ffffff" }, 378 | }, 379 | { 380 | name: "Variables", 381 | scope: "variable.c", 382 | settings: { foreground: "#ffffff" }, 383 | }, 384 | { 385 | name: "Language variables", 386 | scope: "variable.language", 387 | settings: { foreground: "#ffffff" }, 388 | }, 389 | { 390 | name: "Java Variables", 391 | scope: "token.variable.parameter.java", 392 | settings: { foreground: "#ffffff" }, 393 | }, 394 | { 395 | name: "Java Imports", 396 | scope: "import.storage.java", 397 | settings: { foreground: "#ffffff" }, 398 | }, 399 | { 400 | name: "Packages", 401 | scope: "token.package.keyword", 402 | settings: { foreground: "#ff8888" }, 403 | }, 404 | { 405 | name: "Packages", 406 | scope: "token.package", 407 | settings: { foreground: "#ffffff" }, 408 | }, 409 | { 410 | name: "Functions", 411 | scope: [ 412 | "meta.require", 413 | "support.function.any-method", 414 | "variable.function", 415 | ], 416 | settings: { foreground: "#ffffff" }, 417 | }, 418 | { 419 | name: "meta.var.expr", 420 | scope: ["variable.other.readwrite.ts entity.name.function"], 421 | settings: { foreground: "#ffffff", fontStyle: "italic" }, 422 | }, 423 | { 424 | name: "Functions name", 425 | scope: [ 426 | "source.ts meta.var.expr meta.function-call entity.name.function", 427 | "meta.function-call", 428 | ], 429 | settings: { foreground: "#ffa478" }, 430 | }, 431 | { 432 | name: "Classes", 433 | scope: "entity.name.type.namespace", 434 | settings: { foreground: "#ffffff" }, 435 | }, 436 | { 437 | name: "Classes", 438 | scope: "support.class", 439 | settings: { foreground: "#ffffff" }, 440 | }, 441 | { 442 | name: "Class name", 443 | scope: "entity.name.class.identifier.namespace.type", 444 | settings: { foreground: "#ffffff" }, 445 | }, 446 | { 447 | name: "Class name", 448 | scope: [ 449 | "entity.name.class", 450 | "variable.other.class.js", 451 | "variable.other.class.ts", 452 | ], 453 | settings: { foreground: "#ffffff" }, 454 | }, 455 | { 456 | name: "Class name php", 457 | scope: "variable.other.class.php", 458 | settings: { foreground: "#ffffff" }, 459 | }, 460 | { 461 | name: "Keyword Control", 462 | scope: "keyword.control", 463 | settings: { foreground: "#ff8888", fontStyle: "italic" }, 464 | }, 465 | { 466 | name: "Control Elements", 467 | scope: "control.elements, keyword.operator.less", 468 | settings: { foreground: "#14ffde" }, 469 | }, 470 | { 471 | name: "Methods", 472 | scope: "keyword.other.special-method", 473 | settings: { foreground: "#ffa478" }, 474 | }, 475 | { name: "Storage", scope: "storage", settings: { foreground: "#ff8888" } }, 476 | { 477 | name: "Storage JS TS", 478 | scope: "token.storage", 479 | settings: { foreground: "#ff8888" }, 480 | }, 481 | { 482 | name: "Source Js Keyword Operator Delete,source Js Keyword Operator In,source Js Keyword Operator Of,source Js Keyword Operator Instanceof,source Js Keyword Operator New,source Js Keyword Operator Typeof,source Js Keyword Operator Void", 483 | scope: 484 | "keyword.operator.expression.delete,keyword.operator.expression.in,keyword.operator.expression.of,keyword.operator.expression.instanceof,keyword.operator.new,keyword.operator.expression.typeof,keyword.operator.expression.void", 485 | settings: { foreground: "#ff8888" }, 486 | }, 487 | { 488 | name: "Java Storage", 489 | scope: "token.storage.type.java", 490 | settings: { foreground: "#ffffff" }, 491 | }, 492 | { 493 | name: "Support type", 494 | scope: "support.type.property-name", 495 | settings: { foreground: "#ffffff" }, 496 | }, 497 | { 498 | name: "Support type", 499 | scope: "support.constant.property-value", 500 | settings: { foreground: "#ffffff" }, 501 | }, 502 | { 503 | name: "Support type", 504 | scope: "constant.language.import-export-all.ts", 505 | settings: { foreground: "#ffffff" }, 506 | }, 507 | { 508 | name: "Support type", 509 | scope: "support.constant.font-name", 510 | settings: { foreground: "#14ffde" }, 511 | }, 512 | { 513 | name: "Meta tag", 514 | scope: "meta.tag", 515 | settings: { foreground: "#ffffff" }, 516 | }, 517 | { name: "Strings", scope: "string", settings: { foreground: "#83d6ff" } }, 518 | { 519 | name: "Inherited Class", 520 | scope: "entity.other.inherited-class", 521 | settings: { foreground: "#ffffff" }, 522 | }, 523 | { 524 | name: "Constant other symbol", 525 | scope: "constant.other.symbol", 526 | settings: { foreground: "#ffffff" }, 527 | }, 528 | { 529 | name: "Integers", 530 | scope: "constant.numeric", 531 | settings: { foreground: "#ffffff" }, 532 | }, 533 | { 534 | name: "Constants", 535 | scope: "constant", 536 | settings: { foreground: "#14ffde" }, 537 | }, 538 | { 539 | name: "Constants", 540 | scope: "punctuation.definition.constant", 541 | settings: { foreground: "#14ffde" }, 542 | }, 543 | { 544 | name: "Tags", 545 | scope: "entity.name.tag, support.class.component", 546 | settings: { foreground: "#ffa478" }, 547 | }, 548 | { 549 | name: "HTML attributes", 550 | scope: "meta.tag.attributes.tsx", 551 | settings: { foreground: "#ffffff" }, 552 | }, 553 | { 554 | name: "Attributes", 555 | scope: "entity.other.attribute-name", 556 | settings: { foreground: "#ffffff" }, 557 | }, 558 | { 559 | name: "Attribute IDs", 560 | scope: "entity.other.attribute-name.id", 561 | settings: { fontStyle: "normal", foreground: "#ffa478" }, 562 | }, 563 | { 564 | name: "Attribute class", 565 | scope: "entity.other.attribute-name.class.css", 566 | settings: { fontStyle: "normal", foreground: "#14ffde" }, 567 | }, 568 | { 569 | name: "Selector", 570 | scope: "meta.selector", 571 | settings: { foreground: "#ff8888" }, 572 | }, 573 | { 574 | name: "Headings", 575 | scope: "markup.heading", 576 | settings: { foreground: "#ffffff" }, 577 | }, 578 | { 579 | name: "Headings", 580 | scope: 581 | "markup.heading punctuation.definition.heading, entity.name.section", 582 | settings: { foreground: "#ffa478" }, 583 | }, 584 | { 585 | name: "Units", 586 | scope: "keyword.other.unit", 587 | settings: { foreground: "#ffffff" }, 588 | }, 589 | { 590 | name: "Bold", 591 | scope: "markup.bold,todo.bold", 592 | settings: { foreground: "#14ffde" }, 593 | }, 594 | { 595 | name: "Bold", 596 | scope: "punctuation.definition.bold", 597 | settings: { foreground: "#ffffff" }, 598 | }, 599 | { 600 | name: "markup Italic", 601 | scope: "markup.italic, punctuation.definition.italic,todo.emphasis", 602 | settings: { foreground: "#ff8888" }, 603 | }, 604 | { 605 | name: "emphasis md", 606 | scope: "emphasis md", 607 | settings: { foreground: "#ff8888" }, 608 | }, 609 | { 610 | name: "[VSCODE-CUSTOM] Markdown headings", 611 | scope: "entity.name.section.markdown", 612 | settings: { foreground: "#ffffff" }, 613 | }, 614 | { 615 | name: "[VSCODE-CUSTOM] Markdown heading Punctuation Definition", 616 | scope: "punctuation.definition.heading.markdown", 617 | settings: { foreground: "#ffffff" }, 618 | }, 619 | { 620 | name: "punctuation.definition.list.begin.markdown", 621 | scope: "punctuation.definition.list.begin.markdown", 622 | settings: { foreground: "#ffffff" }, 623 | }, 624 | { 625 | name: "[VSCODE-CUSTOM] Markdown heading setext", 626 | scope: "markup.heading.setext", 627 | settings: { foreground: "#ffffff" }, 628 | }, 629 | { 630 | name: "[VSCODE-CUSTOM] Markdown Punctuation Definition Bold", 631 | scope: "punctuation.definition.bold.markdown", 632 | settings: { foreground: "#14ffde" }, 633 | }, 634 | { 635 | name: "[VSCODE-CUSTOM] Markdown Inline Raw", 636 | scope: "markup.inline.raw.markdown", 637 | settings: { foreground: "#83d6ff" }, 638 | }, 639 | { 640 | name: "[VSCODE-CUSTOM] Markdown Inline Raw", 641 | scope: "markup.inline.raw.string.markdown", 642 | settings: { foreground: "#83d6ff" }, 643 | }, 644 | { 645 | name: "[VSCODE-CUSTOM] Markdown List Punctuation Definition", 646 | scope: "punctuation.definition.list.markdown", 647 | settings: { foreground: "#ffffff" }, 648 | }, 649 | { 650 | name: "[VSCODE-CUSTOM] Markdown Punctuation Definition String", 651 | scope: [ 652 | "punctuation.definition.string.begin.markdown", 653 | "punctuation.definition.string.end.markdown", 654 | "punctuation.definition.metadata.markdown", 655 | ], 656 | settings: { foreground: "#ffffff" }, 657 | }, 658 | { 659 | name: "beginning.punctuation.definition.list.markdown", 660 | scope: ["beginning.punctuation.definition.list.markdown"], 661 | settings: { foreground: "#ffffff" }, 662 | }, 663 | { 664 | name: "[VSCODE-CUSTOM] Markdown Punctuation Definition Link", 665 | scope: "punctuation.definition.metadata.markdown", 666 | settings: { foreground: "#ffffff" }, 667 | }, 668 | { 669 | name: "[VSCODE-CUSTOM] Markdown Underline Link/Image", 670 | scope: 671 | "markup.underline.link.markdown,markup.underline.link.image.markdown", 672 | settings: { foreground: "#ff8888" }, 673 | }, 674 | { 675 | name: "[VSCODE-CUSTOM] Markdown Link Title/Description", 676 | scope: 677 | "string.other.link.title.markdown,string.other.link.description.markdown", 678 | settings: { foreground: "#ffa478" }, 679 | }, 680 | { 681 | name: "Regular Expressions", 682 | scope: "string.regexp", 683 | settings: { foreground: "#36ffd0" }, 684 | }, 685 | { 686 | name: "Escape Characters", 687 | scope: 688 | "constant.character.escape.backslash.regexp,constant.character.escape.regexp, constant.other.character-class.regexp, keyword.operator.quantifier.regexp, keyword.control.anchor.regexp", 689 | settings: { foreground: "#36ffd0" }, 690 | }, 691 | { 692 | name: "Embedded", 693 | scope: "punctuation.section.embedded, variable.interpolation", 694 | settings: { foreground: "#ffffff" }, 695 | }, 696 | { 697 | name: "Embedded", 698 | scope: 699 | "punctuation.section.embedded.begin,punctuation.section.embedded.end", 700 | settings: { foreground: "#ff8888" }, 701 | }, 702 | { 703 | name: "illegal", 704 | scope: "invalid.illegal", 705 | settings: { foreground: "#ffffff" }, 706 | }, 707 | { 708 | name: "illegal", 709 | scope: "invalid.illegal.bad-ampersand.html", 710 | settings: { foreground: "#ffffff" }, 711 | }, 712 | { 713 | name: "Broken", 714 | scope: "invalid.broken", 715 | settings: { foreground: "#ffffff" }, 716 | }, 717 | { 718 | name: "Deprecated", 719 | scope: "invalid.deprecated", 720 | settings: { foreground: "#ffffff" }, 721 | }, 722 | { 723 | name: "Unimplemented", 724 | scope: "invalid.unimplemented", 725 | settings: { foreground: "#ffffff" }, 726 | }, 727 | { 728 | name: "Source Json Meta Structure Dictionary Json > String Quoted Json", 729 | scope: "source.json meta.structure.dictionary.json > string.quoted.json", 730 | settings: { foreground: "#ffffff" }, 731 | }, 732 | { 733 | name: "Source Json Meta Structure Dictionary Json > String Quoted Json > Punctuation String", 734 | scope: 735 | "source.json meta.structure.dictionary.json > string.quoted.json > punctuation.string", 736 | settings: { foreground: "#ffffff" }, 737 | }, 738 | { 739 | name: "Source Json Meta Structure Dictionary Json > String Quoted Json > Punctuation String", 740 | scope: 741 | "string.quoted.double.json,punctuation.definition.string.begin.json,punctuation.definition.string.end.json", 742 | settings: { foreground: "#ffffff" }, 743 | }, 744 | { 745 | name: "Source Json Meta Structure Dictionary Json > Value Json > String Quoted Json,source Json Meta Structure Array Json > Value Json > String Quoted Json,source Json Meta Structure Dictionary Json > Value Json > String Quoted Json > Punctuation,source Json Meta Structure Array Json > Value Json > String Quoted Json > Punctuation", 746 | scope: 747 | "source.json meta.structure.dictionary.json > value.json > string.quoted.json,source.json meta.structure.array.json > value.json > string.quoted.json,source.json meta.structure.dictionary.json > value.json > string.quoted.json > punctuation,source.json meta.structure.array.json > value.json > string.quoted.json > punctuation", 748 | settings: { foreground: "#83d6ff" }, 749 | }, 750 | { 751 | name: "Source Json Meta Structure Dictionary Json > Constant Language Json,source Json Meta Structure Array Json > Constant Language Json", 752 | scope: 753 | "source.json meta.structure.dictionary.json > constant.language.json,source.json meta.structure.array.json > constant.language.json", 754 | settings: { foreground: "#ffffff" }, 755 | }, 756 | { 757 | name: "[VSCODE-CUSTOM] JSON Property Name", 758 | scope: "support.type.property-name.json", 759 | settings: { foreground: "#FFA478" }, 760 | }, 761 | { 762 | name: "laravel blade tag", 763 | scope: 764 | "text.html.laravel-blade source.php.embedded.line.html entity.name.tag.laravel-blade", 765 | settings: { foreground: "#ff8888" }, 766 | }, 767 | { 768 | name: "laravel blade @", 769 | scope: 770 | "text.html.laravel-blade source.php.embedded.line.html support.constant.laravel-blade", 771 | settings: { foreground: "#ff8888" }, 772 | }, 773 | { 774 | name: "use statement for other classes", 775 | scope: 776 | "support.other.namespace.use.php,support.other.namespace.use-as.php,support.other.namespace.php,entity.other.alias.php,meta.interface.php", 777 | settings: { foreground: "#ffffff" }, 778 | }, 779 | { 780 | name: "error suppression", 781 | scope: "keyword.operator.error-control.php", 782 | settings: { foreground: "#ff8888" }, 783 | }, 784 | { 785 | name: "php instanceof", 786 | scope: "keyword.operator.type.php", 787 | settings: { foreground: "#ff8888" }, 788 | }, 789 | { 790 | name: "style double quoted array index normal begin", 791 | scope: "punctuation.section.array.begin.php", 792 | settings: { foreground: "#ffffff" }, 793 | }, 794 | { 795 | name: "style double quoted array index normal end", 796 | scope: "punctuation.section.array.end.php", 797 | settings: { foreground: "#ffffff" }, 798 | }, 799 | { 800 | name: "php illegal.non-null-typehinted", 801 | scope: "invalid.illegal.non-null-typehinted.php", 802 | settings: { foreground: "#f44747" }, 803 | }, 804 | { 805 | name: "php types", 806 | scope: 807 | "storage.type.php,meta.other.type.phpdoc.php,keyword.other.type.php,keyword.other.array.phpdoc.php", 808 | settings: { foreground: "#ffffff" }, 809 | }, 810 | { 811 | name: "php call-function", 812 | scope: 813 | "meta.function-call.php,meta.function-call.object.php,meta.function-call.static.php", 814 | settings: { foreground: "#ffa478" }, 815 | }, 816 | { 817 | name: "php function-resets", 818 | scope: 819 | "punctuation.definition.parameters.begin.bracket.round.php,punctuation.definition.parameters.end.bracket.round.php,punctuation.separator.delimiter.php,punctuation.section.scope.begin.php,punctuation.section.scope.end.php,punctuation.terminator.expression.php,punctuation.definition.arguments.begin.bracket.round.php,punctuation.definition.arguments.end.bracket.round.php,punctuation.definition.storage-type.begin.bracket.round.php,punctuation.definition.storage-type.end.bracket.round.php,punctuation.definition.array.begin.bracket.round.php,punctuation.definition.array.end.bracket.round.php,punctuation.definition.begin.bracket.round.php,punctuation.definition.end.bracket.round.php,punctuation.definition.begin.bracket.curly.php,punctuation.definition.end.bracket.curly.php,punctuation.definition.section.switch-block.end.bracket.curly.php,punctuation.definition.section.switch-block.start.bracket.curly.php,punctuation.definition.section.switch-block.begin.bracket.curly.php,punctuation.definition.section.switch-block.end.bracket.curly.php", 820 | settings: { foreground: "#ffffff" }, 821 | }, 822 | { 823 | name: "support php constants", 824 | scope: "support.constant.core.rust", 825 | settings: { foreground: "#14ffde" }, 826 | }, 827 | { 828 | name: "support php constants", 829 | scope: 830 | "support.constant.ext.php,support.constant.std.php,support.constant.core.php,support.constant.parser-token.php", 831 | settings: { foreground: "#14ffde" }, 832 | }, 833 | { 834 | name: "php goto", 835 | scope: "entity.name.goto-label.php,support.other.php", 836 | settings: { foreground: "#ffa478" }, 837 | }, 838 | { 839 | name: "php logical/bitwise operator", 840 | scope: 841 | "keyword.operator.logical.php,keyword.operator.bitwise.php,keyword.operator.arithmetic.php", 842 | settings: { foreground: "#ffffff" }, 843 | }, 844 | { 845 | name: "php regexp operator", 846 | scope: "keyword.operator.regexp.php", 847 | settings: { foreground: "#ff8888" }, 848 | }, 849 | { 850 | name: "php comparison", 851 | scope: "keyword.operator.comparison.php", 852 | settings: { foreground: "#ffffff" }, 853 | }, 854 | { 855 | name: "php heredoc/nowdoc", 856 | scope: "keyword.operator.heredoc.php,keyword.operator.nowdoc.php", 857 | settings: { foreground: "#ff8888" }, 858 | }, 859 | { 860 | name: "python function decorator @", 861 | scope: "meta.function.decorator.python", 862 | settings: { foreground: "#ffa478" }, 863 | }, 864 | { 865 | name: "python function support", 866 | scope: 867 | "support.token.decorator.python,meta.function.decorator.identifier.python", 868 | settings: { foreground: "#ffffff" }, 869 | }, 870 | { 871 | name: "parameter function js/ts", 872 | scope: "function.parameter", 873 | settings: { foreground: "#ffffff" }, 874 | }, 875 | { 876 | name: "brace function", 877 | scope: "function.brace", 878 | settings: { foreground: "#ffffff" }, 879 | }, 880 | { 881 | name: "parameter function ruby cs", 882 | scope: "function.parameter.ruby, function.parameter.cs", 883 | settings: { foreground: "#ffffff" }, 884 | }, 885 | { 886 | name: "constant.language.symbol.ruby", 887 | scope: "constant.language.symbol.ruby", 888 | settings: { foreground: "#ffffff" }, 889 | }, 890 | { 891 | name: "rgb-value", 892 | scope: "rgb-value", 893 | settings: { foreground: "#ffffff" }, 894 | }, 895 | { 896 | name: "rgb value", 897 | scope: "inline-color-decoration rgb-value", 898 | settings: { foreground: "#14ffde" }, 899 | }, 900 | { 901 | name: "rgb value less", 902 | scope: "less rgb-value", 903 | settings: { foreground: "#14ffde" }, 904 | }, 905 | { 906 | name: "sass selector", 907 | scope: "selector.sass", 908 | settings: { foreground: "#ffffff" }, 909 | }, 910 | { 911 | name: "ts primitive/builtin types", 912 | scope: 913 | "support.type.primitive.ts,support.type.builtin.ts,support.type.primitive.tsx,support.type.builtin.tsx", 914 | settings: { foreground: "#14ffde" }, 915 | }, 916 | { 917 | name: "Typescript porperty definition", 918 | scope: [ 919 | "meta.definition.property", 920 | "meta.definition.property variable.object.property", 921 | ], 922 | settings: { foreground: "#ffffff" }, 923 | }, 924 | { 925 | name: "block scope", 926 | scope: "block.scope.end,block.scope.begin", 927 | settings: { foreground: "#ffffff" }, 928 | }, 929 | { 930 | name: "cs storage type", 931 | scope: "storage.type.cs", 932 | settings: { foreground: "#ffffff" }, 933 | }, 934 | { 935 | name: "cs local variable", 936 | scope: "entity.name.variable.local.cs", 937 | settings: { foreground: "#ffffff" }, 938 | }, 939 | { scope: "token.info-token", settings: { foreground: "#ffa478" } }, 940 | { scope: "token.warn-token", settings: { foreground: "#14ffde" } }, 941 | { scope: "token.error-token", settings: { foreground: "#f44747" } }, 942 | { scope: "token.debug-token", settings: { foreground: "#ff8888" } }, 943 | { 944 | name: "String interpolation", 945 | scope: [ 946 | "punctuation.definition.template-expression.begin", 947 | "punctuation.definition.template-expression.end", 948 | "punctuation.section.embedded", 949 | ], 950 | settings: { foreground: "#ff8888" }, 951 | }, 952 | { 953 | scope: "entity.name.function.tagged-template.tsx", 954 | settings: { foreground: "#FFA478" }, 955 | }, 956 | { 957 | name: "Reset JavaScript string interpolation expression", 958 | scope: ["meta.template.expression"], 959 | settings: { foreground: "#ffffff" }, 960 | }, 961 | { 962 | name: "Import module JS", 963 | scope: ["keyword.operator.module"], 964 | settings: { foreground: "#ff8888" }, 965 | }, 966 | { 967 | name: "js Flowtype", 968 | scope: ["support.type.type.flowtype"], 969 | settings: { foreground: "#ffa478" }, 970 | }, 971 | { 972 | name: "js Flow", 973 | scope: ["support.type.primitive"], 974 | settings: { foreground: "#ffffff" }, 975 | }, 976 | { 977 | name: "js class prop", 978 | scope: ["meta.property.object"], 979 | settings: { foreground: "#ffffff" }, 980 | }, 981 | { 982 | name: "js func parameter", 983 | scope: ["variable.parameter.function.js"], 984 | settings: { foreground: "#ffffff" }, 985 | }, 986 | { 987 | name: "js template literals begin", 988 | scope: ["keyword.other.template.begin"], 989 | settings: { foreground: "#83d6ff" }, 990 | }, 991 | { 992 | name: "js template literals end", 993 | scope: ["keyword.other.template.end"], 994 | settings: { foreground: "#83d6ff" }, 995 | }, 996 | { 997 | name: "js template literals variable braces begin", 998 | scope: ["keyword.other.substitution.begin"], 999 | settings: { foreground: "#83d6ff" }, 1000 | }, 1001 | { 1002 | name: "js template literals variable braces end", 1003 | scope: ["keyword.other.substitution.end"], 1004 | settings: { foreground: "#83d6ff" }, 1005 | }, 1006 | { 1007 | name: "js operator.assignment", 1008 | scope: ["keyword.operator.assignment"], 1009 | settings: { foreground: "#ffffff" }, 1010 | }, 1011 | { 1012 | name: "go operator", 1013 | scope: ["keyword.operator.assignment.go"], 1014 | settings: { foreground: "#ffffff" }, 1015 | }, 1016 | { 1017 | name: "go operator", 1018 | scope: ["keyword.operator.arithmetic.go", "keyword.operator.address.go"], 1019 | settings: { foreground: "#ff8888" }, 1020 | }, 1021 | { 1022 | name: "Go package name", 1023 | scope: ["entity.name.package.go"], 1024 | settings: { foreground: "#ffffff" }, 1025 | }, 1026 | { 1027 | name: "elm prelude", 1028 | scope: ["support.type.prelude.elm"], 1029 | settings: { foreground: "#ffffff" }, 1030 | }, 1031 | { 1032 | name: "elm constant", 1033 | scope: ["support.constant.elm"], 1034 | settings: { foreground: "#14ffde" }, 1035 | }, 1036 | { 1037 | name: "template literal", 1038 | scope: ["punctuation.quasi.element"], 1039 | settings: { foreground: "#ff8888" }, 1040 | }, 1041 | { 1042 | name: "html/pug (jade) escaped characters and entities", 1043 | scope: ["constant.character.entity"], 1044 | settings: { foreground: "#ffffff" }, 1045 | }, 1046 | { 1047 | name: "styling css pseudo-elements/classes to be able to differentiate from classes which are the same colour", 1048 | scope: [ 1049 | "entity.other.attribute-name.pseudo-element", 1050 | "entity.other.attribute-name.pseudo-class", 1051 | ], 1052 | settings: { foreground: "#ffffff" }, 1053 | }, 1054 | { 1055 | name: "Clojure globals", 1056 | scope: ["entity.global.clojure"], 1057 | settings: { foreground: "#ffffff" }, 1058 | }, 1059 | { 1060 | name: "Clojure symbols", 1061 | scope: ["meta.symbol.clojure"], 1062 | settings: { foreground: "#ffffff" }, 1063 | }, 1064 | { 1065 | name: "Clojure constants", 1066 | scope: ["constant.keyword.clojure"], 1067 | settings: { foreground: "#ffffff" }, 1068 | }, 1069 | { 1070 | name: "CoffeeScript Function Argument", 1071 | scope: ["meta.arguments.coffee", "variable.parameter.function.coffee"], 1072 | settings: { foreground: "#ffffff" }, 1073 | }, 1074 | { 1075 | name: "Ini Default Text", 1076 | scope: ["source.ini"], 1077 | settings: { foreground: "#83d6ff" }, 1078 | }, 1079 | { 1080 | name: "Makefile prerequisities", 1081 | scope: ["meta.scope.prerequisites.makefile"], 1082 | settings: { foreground: "#ffffff" }, 1083 | }, 1084 | { 1085 | name: "Makefile text colour", 1086 | scope: ["source.makefile"], 1087 | settings: { foreground: "#ffffff" }, 1088 | }, 1089 | { 1090 | name: "Groovy import names", 1091 | scope: ["storage.modifier.import.groovy"], 1092 | settings: { foreground: "#ffffff" }, 1093 | }, 1094 | { 1095 | name: "Groovy Methods", 1096 | scope: ["meta.method.groovy"], 1097 | settings: { foreground: "#ffa478" }, 1098 | }, 1099 | { 1100 | name: "Groovy Variables", 1101 | scope: ["meta.definition.variable.name.groovy"], 1102 | settings: { foreground: "#ffffff" }, 1103 | }, 1104 | { 1105 | name: "Groovy Inheritance", 1106 | scope: ["meta.definition.class.inherited.classes.groovy"], 1107 | settings: { foreground: "#83d6ff" }, 1108 | }, 1109 | { 1110 | name: "HLSL Semantic", 1111 | scope: ["support.variable.semantic.hlsl"], 1112 | settings: { foreground: "#ffffff" }, 1113 | }, 1114 | { 1115 | name: "HLSL Types", 1116 | scope: [ 1117 | "support.type.texture.hlsl", 1118 | "support.type.sampler.hlsl", 1119 | "support.type.object.hlsl", 1120 | "support.type.object.rw.hlsl", 1121 | "support.type.fx.hlsl", 1122 | "support.type.object.hlsl", 1123 | ], 1124 | settings: { foreground: "#ff8888" }, 1125 | }, 1126 | { 1127 | name: "SQL Variables", 1128 | scope: ["text.variable", "text.bracketed"], 1129 | settings: { foreground: "#ffffff" }, 1130 | }, 1131 | { 1132 | name: "types", 1133 | scope: ["support.type.swift", "support.type.vb.asp"], 1134 | settings: { foreground: "#ffffff" }, 1135 | }, 1136 | { 1137 | name: "heading 1, keyword", 1138 | scope: ["entity.name.function.xi"], 1139 | settings: { foreground: "#ffffff" }, 1140 | }, 1141 | { 1142 | name: "heading 2, callable", 1143 | scope: ["entity.name.class.xi"], 1144 | settings: { foreground: "#ffffff" }, 1145 | }, 1146 | { 1147 | name: "heading 3, property", 1148 | scope: ["constant.character.character-class.regexp.xi"], 1149 | settings: { foreground: "#ffffff" }, 1150 | }, 1151 | { 1152 | name: "heading 4, type, class, interface", 1153 | scope: ["constant.regexp.xi"], 1154 | settings: { foreground: "#ff8888" }, 1155 | }, 1156 | { 1157 | name: "heading 5, enums, preprocessor, constant, decorator", 1158 | scope: ["keyword.control.xi"], 1159 | settings: { foreground: "#ffffff" }, 1160 | }, 1161 | { 1162 | name: "heading 6, number", 1163 | scope: ["invalid.xi"], 1164 | settings: { foreground: "#ffffff" }, 1165 | }, 1166 | { 1167 | name: "string", 1168 | scope: ["beginning.punctuation.definition.quote.markdown.xi"], 1169 | settings: { foreground: "#83d6ff" }, 1170 | }, 1171 | { 1172 | name: "comments", 1173 | scope: ["beginning.punctuation.definition.list.markdown.xi"], 1174 | settings: { foreground: "#ffffff44" }, 1175 | }, 1176 | { 1177 | name: "link", 1178 | scope: ["constant.character.xi"], 1179 | settings: { foreground: "#ffa478" }, 1180 | }, 1181 | { 1182 | name: "accent", 1183 | scope: ["accent.xi"], 1184 | settings: { foreground: "#ffa478" }, 1185 | }, 1186 | { 1187 | name: "wikiword", 1188 | scope: ["wikiword.xi"], 1189 | settings: { foreground: "#14ffde" }, 1190 | }, 1191 | { 1192 | name: "language operators like '+', '-' etc", 1193 | scope: ["constant.other.color.rgb-value.xi"], 1194 | settings: { foreground: "#ffffff" }, 1195 | }, 1196 | { 1197 | name: "elements to dim", 1198 | scope: ["punctuation.definition.tag.xi"], 1199 | settings: { foreground: "#ffffff44" }, 1200 | }, 1201 | { 1202 | name: "C++/C#", 1203 | scope: [ 1204 | "entity.name.label.cs", 1205 | "entity.name.scope-resolution.function.call", 1206 | "entity.name.scope-resolution.function.definition", 1207 | ], 1208 | settings: { foreground: "#ffffff" }, 1209 | }, 1210 | { 1211 | name: "Markdown underscore-style headers", 1212 | scope: [ 1213 | "entity.name.label.cs", 1214 | "markup.heading.setext.1.markdown", 1215 | "markup.heading.setext.2.markdown", 1216 | ], 1217 | settings: { foreground: "#ffffff" }, 1218 | }, 1219 | { 1220 | name: "Function punctuation", 1221 | scope: 1222 | "punctuation.definition.typeparameters, storage.type.function.arrow,punctuation.definition.block, punctuation.accessor", 1223 | settings: { foreground: "#ffffff" }, 1224 | }, 1225 | { 1226 | name: "Comments", 1227 | scope: "comment, punctuation.definition.comment", 1228 | settings: { fontStyle: "italic", foreground: "#ffffff44" }, 1229 | }, 1230 | { 1231 | name: "[VSCODE-CUSTOM] Markdown Quote", 1232 | scope: "markup.quote.markdown", 1233 | settings: { foreground: "#ffffff44" }, 1234 | }, 1235 | { 1236 | name: "punctuation.definition.block.sequence.item.yaml", 1237 | scope: "punctuation.definition.block.sequence.item.yaml", 1238 | settings: { foreground: "#ffffff" }, 1239 | }, 1240 | { 1241 | scope: ["constant.language.symbol.elixir"], 1242 | settings: { foreground: "#ffffff" }, 1243 | }, 1244 | { 1245 | name: "js/ts italic", 1246 | scope: 1247 | "entity.other.attribute-name.js,entity.other.attribute-name.ts,entity.other.attribute-name.jsx,entity.other.attribute-name.tsx,variable.language.super", 1248 | settings: { fontStyle: "italic" }, 1249 | }, 1250 | { 1251 | name: "comment", 1252 | scope: "comment.line.double-slash,comment.block.documentation", 1253 | settings: { fontStyle: "italic" }, 1254 | }, 1255 | { 1256 | name: "Python Keyword Control", 1257 | scope: "keyword.control.import.python,keyword.control.flow.python", 1258 | settings: { fontStyle: "italic" }, 1259 | }, 1260 | { 1261 | name: "markup.italic.markdown", 1262 | scope: "markup.italic.markdown", 1263 | settings: { fontStyle: "italic" }, 1264 | }, 1265 | { scope: "storage.type.function.arrow", settings: { fontStyle: "" } }, 1266 | { 1267 | scope: "string.quoted, string.template", 1268 | settings: { foreground: "#c6ff90" }, 1269 | }, 1270 | { 1271 | scope: 1272 | "support.type, entity.name.type.ts, support.type.primitive.ts, entity.name.type, constant, entity.other.inherited-class", 1273 | settings: { foreground: "#35eeff" }, 1274 | }, 1275 | { 1276 | scope: "storage, keyword.control, storage.type", 1277 | settings: { fontStyle: "", foreground: "#ff8888" }, 1278 | }, 1279 | { 1280 | scope: "support.variable, support.class, entity.name.tag", 1281 | settings: { 1282 | fontStyle: "bold", 1283 | // foreground: "#ff7474", 1284 | foreground: "#F39F47", 1285 | }, 1286 | }, 1287 | { scope: "support.function.lua", settings: { foreground: "#ff8888" } }, 1288 | { 1289 | scope: "support.function.library.lua", 1290 | settings: { foreground: "#ff8888" }, 1291 | }, 1292 | ], 1293 | colors: { 1294 | "foreground": "#ffffff", 1295 | "focusBorder": "#00000000", 1296 | "selection.background": "#ffd86e", 1297 | "scrollbar.shadow": "#00000000", 1298 | "activityBar.foreground": "#ffffff", 1299 | "activityBar.background": "#23252e", 1300 | "activityBar.inactiveForeground": "#ffffff11", 1301 | "activityBarBadge.foreground": "#23252e", 1302 | "activityBarBadge.background": "#ffd86e", 1303 | "activityBar.border": "#00000000", 1304 | "activityBar.activeBackground": "#00000000", 1305 | "sideBar.background": "#23252e", 1306 | "sideBar.foreground": "#ffffff", 1307 | "sideBarSectionHeader.background": "#00000000", 1308 | "sideBarSectionHeader.foreground": "#ffffff", 1309 | "sideBarSectionHeader.border": "#00000000", 1310 | "sideBarTitle.foreground": "#ffd86e", 1311 | "sideBar.border": "#1c1e25", 1312 | "list.inactiveSelectionBackground": "#272a34", 1313 | "list.inactiveSelectionForeground": "#ffffff", 1314 | "list.hoverBackground": "#00000022", 1315 | "list.hoverForeground": "#ffffff", 1316 | "list.activeSelectionBackground": "#ffd86e", 1317 | "list.activeSelectionForeground": "#23252e", 1318 | "list.activeSelectionIconForeground": "#23252e", 1319 | "tree.indentGuidesStroke": "#ffffff22", 1320 | "list.dropBackground": "#00000044", 1321 | "list.highlightForeground": "#ffffff", 1322 | "list.focusHighlightForeground": "#23252e", 1323 | "list.focusBackground": "#00000044", 1324 | "list.focusForeground": "#ffffff", 1325 | "listFilterWidget.background": "#1c1e25", 1326 | "listFilterWidget.outline": "#00000000", 1327 | "listFilterWidget.noMatchesOutline": "#ff7062", 1328 | "statusBar.foreground": "#ffffff", 1329 | "statusBar.background": "#23252e", 1330 | "statusBarItem.hoverBackground": "#00000033", 1331 | "statusBar.border": "#00000000", 1332 | "statusBar.debuggingBackground": "#ffd86e", 1333 | "statusBar.debuggingForeground": "#23252e", 1334 | "statusBar.debuggingBorder": "#ffd86e", 1335 | "statusBar.noFolderBackground": "#ff7062", 1336 | "statusBar.noFolderForeground": "#23252e", 1337 | "statusBar.noFolderBorder": "#23252e", 1338 | "statusBarItem.remoteBackground": "#ffd86e", 1339 | "statusBarItem.remoteForeground": "#23252e", 1340 | "statusBarItem.activeBackground": "#FFFFFF25", 1341 | "titleBar.activeBackground": "#23252e", 1342 | "titleBar.activeForeground": "#ffffff", 1343 | "titleBar.inactiveBackground": "#23252e", 1344 | "titleBar.inactiveForeground": "#ffffff44", 1345 | "titleBar.border": "#00000000", 1346 | "editorLightBulb.foreground": "#FFFFFF", 1347 | "editorLightBulbAutoFix.foreground": "#FFFFFF", 1348 | "menubar.selectionForeground": "#ffffff", 1349 | "menubar.selectionBackground": "#00000033", 1350 | "menubar.selectionBorder": "#00000000", 1351 | "menu.foreground": "#ffffff", 1352 | "menu.background": "#272a34", 1353 | "menu.selectionForeground": "#23252e", 1354 | "menu.selectionBackground": "#F6CA52", 1355 | "menu.selectionBorder": "#00000000", 1356 | "menu.separatorBackground": "#1c1e25", 1357 | "menu.border": "#1c1e25", 1358 | "button.background": "#ffd86e", 1359 | "button.foreground": "#23252e", 1360 | "button.hoverBackground": "#ffbc1f", 1361 | "button.secondaryForeground": "#ffffff", 1362 | "button.secondaryBackground": "#1c1e25", 1363 | "button.secondaryHoverBackground": "#272a34", 1364 | "input.background": "#272a34", 1365 | "input.border": "#00000000", 1366 | "input.foreground": "#ffffff", 1367 | "inputOption.activeBackground": "#00000000", 1368 | "inputOption.activeBorder": "#00000000", 1369 | "inputOption.activeForeground": "#ffd86e", 1370 | "input.placeholderForeground": "#ffffff55", 1371 | "textLink.foreground": "#ffd86e", 1372 | "editor.background": "#ffffff00", 1373 | "editor.foreground": "#ffffff", 1374 | "editorLineNumber.foreground": "#ffffff33", 1375 | "editorCursor.foreground": "#ffffff", 1376 | "editorCursor.background": "#23252e", 1377 | "editor.selectionBackground": "#264f78", 1378 | "editor.inactiveSelectionBackground": "#3a3d41", 1379 | "editorWhitespace.foreground": "#e3e4e229", 1380 | "editor.selectionHighlightBackground": "#add6ff26", 1381 | "editor.selectionHighlightBorder": "#495F77", 1382 | "editor.findMatchBackground": "#515c6a", 1383 | "editor.findMatchBorder": "#74879f", 1384 | "editor.findMatchHighlightBackground": "#ffffff11", 1385 | "editor.findMatchHighlightBorder": "#ffffff00", 1386 | "editor.findRangeHighlightBackground": "#3a3d4166", 1387 | "editor.findRangeHighlightBorder": "#ffffff00", 1388 | "editor.rangeHighlightBackground": "#ffffff0b", 1389 | "editor.rangeHighlightBorder": "#ffffff00", 1390 | "editor.hoverHighlightBackground": "#264f7840", 1391 | "editor.wordHighlightStrongBackground": "#ffffff11", 1392 | "editor.wordHighlightStrongBorder": "#00000000", 1393 | "editor.wordHighlightBackground": "#ffffff11", 1394 | "editor.wordHighlightBorder": "#00000000", 1395 | "editor.lineHighlightBackground": "#00000033", 1396 | "editor.lineHighlightBorder": "#00000000", 1397 | "editorLineNumber.activeForeground": "#ffffff", 1398 | "editorLink.activeForeground": "#4eb4ff", 1399 | "editorIndentGuide.background": "#ffffff11", 1400 | "editorIndentGuide.activeBackground": "#ffffff33", 1401 | "editorRuler.foreground": "#ffffff11", 1402 | "editorBracketMatch.background": "#ffffff11", 1403 | "editorBracketMatch.border": "#00000000", 1404 | "editor.foldBackground": "#00000044", 1405 | "editorOverviewRuler.background": "#00000000", 1406 | "editorOverviewRuler.border": "#00000000", 1407 | "editorError.foreground": "#f29086", 1408 | "editorError.background": "#00000000", 1409 | "editorError.border": "#00000000", 1410 | "editorWarning.foreground": "#ffd1a8", 1411 | "editorWarning.background": "#00000000", 1412 | "editorWarning.border": "#00000000", 1413 | "editorInfo.foreground": "#ffffff33", 1414 | "editorInfo.background": "#00000000", 1415 | "editorInfo.border": "#00000000", 1416 | "editorGutter.background": "#23252e", 1417 | "editorGutter.modifiedBackground": "#2dced0", 1418 | "editorGutter.addedBackground": "#5bf29a", 1419 | "editorGutter.deletedBackground": "#ff7062", 1420 | "editorGutter.foldingControlForeground": "#ffffff33", 1421 | "editorCodeLens.foreground": "#ffffff33", 1422 | "editorGroup.border": "#1c1e25", 1423 | "diffEditor.diagonalFill": "#ffffff11", 1424 | "diffEditor.insertedTextBackground": "#234e55", 1425 | "diffEditor.insertedTextBorder": "#00000000", 1426 | "diffEditor.removedTextBackground": "#4f3438", 1427 | "diffEditor.removedTextBorder": "#00000000", 1428 | "diffEditor.border": "#1c1e25", 1429 | "panel.background": "#23252e", 1430 | "panel.border": "#1c1e25", 1431 | "panelTitle.activeBorder": "#ffd86e", 1432 | "panelTitle.activeForeground": "#ffd86e", 1433 | "panelTitle.inactiveForeground": "#ffffff44", 1434 | "badge.background": "#ffd86e", 1435 | "badge.foreground": "#23252e", 1436 | "terminal.foreground": "#ffffff", 1437 | "terminal.selectionBackground": "#ffffff11", 1438 | "terminalCursor.background": "#23252e", 1439 | "terminalCursor.foreground": "#ffd86e", 1440 | "terminal.border": "#1c1e25", 1441 | "terminal.ansiBlack": "#313744", 1442 | "terminal.ansiBlue": "#4cb2ff", 1443 | "terminal.ansiBrightBlack": "#556570", 1444 | "terminal.ansiBrightBlue": "#4cb2ff", 1445 | "terminal.ansiBrightCyan": "#24e8ea", 1446 | "terminal.ansiBrightGreen": "#5bf29a", 1447 | "terminal.ansiBrightMagenta": "#a7a7ff", 1448 | "terminal.ansiBrightRed": "#ff7272", 1449 | "terminal.ansiBrightWhite": "#ffffff", 1450 | "terminal.ansiBrightYellow": "#ffd04f", 1451 | "terminal.ansiCyan": "#2dced0", 1452 | "terminal.ansiGreen": "#5ecb8c", 1453 | "terminal.ansiMagenta": "#9292ff", 1454 | "terminal.ansiRed": "#e17e85", 1455 | "terminal.ansiWhite": "#f8f8f8", 1456 | "terminal.ansiYellow": "#f6ca52", 1457 | "breadcrumb.background": "#23252e", 1458 | "breadcrumb.foreground": "#ffffff44", 1459 | "breadcrumb.focusForeground": "#ffffff", 1460 | "editorGroupHeader.border": "#1c1e25", 1461 | "editorGroupHeader.tabsBackground": "#23252e", 1462 | "editorGroupHeader.tabsBorder": "#00000000", 1463 | "tab.activeForeground": "#ffffff", 1464 | "tab.border": "#1c1e25", 1465 | "tab.activeBackground": "#272a34", 1466 | "tab.activeBorder": "#00000000", 1467 | "tab.activeBorderTop": "#00000000", 1468 | "tab.inactiveBackground": "#23252e", 1469 | "tab.inactiveForeground": "#ffffff44", 1470 | "tab.hoverBackground": "#00000033", 1471 | "tab.hoverForeground": "#ffffff44", 1472 | "tab.hoverBorder": "#00000000", 1473 | "scrollbarSlider.background": "#1c1e25", 1474 | "scrollbarSlider.hoverBackground": "#272a34", 1475 | "scrollbarSlider.activeBackground": "#272a34", 1476 | "progressBar.background": "#ffd86e", 1477 | "widget.shadow": "#00000000", 1478 | "editorWidget.foreground": "#ffffff", 1479 | "editorWidget.background": "#272a34", 1480 | "editorWidget.resizeBorder": "#1c1e25", 1481 | "pickerGroup.border": "#ffffff11", 1482 | "pickerGroup.foreground": "#ffd86e", 1483 | "debugToolBar.background": "#272a34", 1484 | "debugToolBar.border": "#00000000", 1485 | "notifications.foreground": "#ffffff", 1486 | "notifications.background": "#272a34", 1487 | "notificationToast.border": "#00000000", 1488 | "notificationsErrorIcon.foreground": "#ff7062", 1489 | "notificationsWarningIcon.foreground": "#ffd86e", 1490 | "notificationsInfoIcon.foreground": "#2dced0", 1491 | "notificationCenter.border": "#1c1e25", 1492 | "notificationCenterHeader.foreground": "#ffffff", 1493 | "notificationCenterHeader.background": "#272a34", 1494 | "notifications.border": "#1c1e25", 1495 | "gitDecoration.addedResourceForeground": "#5bf29a", 1496 | "gitDecoration.conflictingResourceForeground": "#9292ff", 1497 | "gitDecoration.deletedResourceForeground": "#ff7272", 1498 | "gitDecoration.ignoredResourceForeground": "#ffffff33", 1499 | "gitDecoration.modifiedResourceForeground": "#ffc773", 1500 | "gitDecoration.stageDeletedResourceForeground": "#ff7062", 1501 | "gitDecoration.stageModifiedResourceForeground": "#e2c08d", 1502 | "gitDecoration.submoduleResourceForeground": "#a7a7ff", 1503 | "gitDecoration.untrackedResourceForeground": "#5ecb8c", 1504 | "editorMarkerNavigation.background": "#1c1e25", 1505 | "editorMarkerNavigationError.background": "#f29086", 1506 | "editorMarkerNavigationWarning.background": "#ffd86e", 1507 | "editorMarkerNavigationInfo.background": "#2dced0", 1508 | "merge.currentHeaderBackground": "#5bf29a55", 1509 | "merge.currentContentBackground": "#5bf29a22", 1510 | "merge.incomingHeaderBackground": "#2dced055", 1511 | "merge.incomingContentBackground": "#2dced022", 1512 | "merge.commonHeaderBackground": "#1c1e25", 1513 | "merge.commonContentBackground": "#1c1e25", 1514 | "editorSuggestWidget.background": "#23252e", 1515 | "editorSuggestWidget.border": "#1c1e25", 1516 | "editorSuggestWidget.foreground": "#ffffff55", 1517 | "editorSuggestWidget.highlightForeground": "#ffffff", 1518 | "editorSuggestWidget.selectedBackground": "#00000044", 1519 | "editorSuggestWidget.selectedIconForeground": "#F6CA52", 1520 | "editorSuggestWidget.selectedForeground": "#FFFFFF", 1521 | "editorSuggestWidget.focusHighlightForeground": "#F6CA52", 1522 | "editorHoverWidget.foreground": "#ffffff55", 1523 | "editorHoverWidget.background": "#272a34", 1524 | "editorHoverWidget.border": "#1c1e25", 1525 | "peekView.border": "#007acc", 1526 | "peekViewEditor.background": "#001f33", 1527 | "peekViewEditorGutter.background": "#001f33", 1528 | "peekViewEditor.matchHighlightBackground": "#ff8f0099", 1529 | "peekViewEditor.matchHighlightBorder": "#ee931e", 1530 | "peekViewResult.background": "#252526", 1531 | "peekViewResult.fileForeground": "#ffffff", 1532 | "peekViewResult.lineForeground": "#bbbbbb", 1533 | "peekViewResult.matchHighlightBackground": "#ea5c004d", 1534 | "peekViewResult.selectionBackground": "#3399ff33", 1535 | "peekViewResult.selectionForeground": "#ffffff", 1536 | "peekViewTitle.background": "#1e1e1e", 1537 | "peekViewTitleDescription.foreground": "#ccccccb3", 1538 | "peekViewTitleLabel.foreground": "#ffffff", 1539 | "icon.foreground": "#ffffff", 1540 | "checkbox.background": "#272a34", 1541 | "checkbox.foreground": "#ffffff", 1542 | "checkbox.border": "#00000000", 1543 | "dropdown.background": "#272a34", 1544 | "dropdown.foreground": "#ffffff", 1545 | "dropdown.border": "#00000000", 1546 | "minimapGutter.addedBackground": "#5bf29a", 1547 | "minimapGutter.modifiedBackground": "#2dced0", 1548 | "minimapGutter.deletedBackground": "#ff7062", 1549 | "minimap.findMatchHighlight": "#515c6a", 1550 | "minimap.selectionHighlight": "#264f78", 1551 | "minimap.errorHighlight": "#f29086", 1552 | "minimap.warningHighlight": "#ffd86e", 1553 | "minimap.background": "#23252e", 1554 | "sideBar.dropBackground": "#00000044", 1555 | "editorGroup.emptyBackground": "#23252e", 1556 | "panelSection.border": "#1c1e25", 1557 | "settings.headerForeground": "#ffffff", 1558 | "settings.focusedRowBackground": "#ffffff07", 1559 | "walkThrough.embeddedEditorBackground": "#00000050", 1560 | "breadcrumb.activeSelectionForeground": "#ffffff", 1561 | "editorGutter.commentRangeForeground": "#ffffff33", 1562 | "debugExceptionWidget.background": "#272a34", 1563 | "debugExceptionWidget.border": "#00000000", 1564 | "editorBracketHighlight.foreground1": "#cdd2e9", 1565 | "editorBracketHighlight.foreground2": "#fad185", 1566 | "editorBracketHighlight.foreground3": "#35DEE9", 1567 | "editorBracketHighlight.foreground4": "#e78f9e", 1568 | }, 1569 | }; 1570 | --------------------------------------------------------------------------------