diff --git a/app/globals.css b/app/globals.css index 3384db0..5738526 100644 --- a/app/globals.css +++ b/app/globals.css @@ -29,6 +29,10 @@ --unit-5-light: #f0e7ff; --unit-5-dark: #5b21b6; + --unit-6: #4f46e5; + --unit-6-light: #eef2ff; + --unit-6-dark: #3730a3; + --correct: #16a34a; --correct-light: #dcfce7; --incorrect: #dc2626; @@ -66,6 +70,9 @@ --color-unit-5: var(--unit-5); --color-unit-5-light: var(--unit-5-light); --color-unit-5-dark: var(--unit-5-dark); + --color-unit-6: var(--unit-6); + --color-unit-6-light: var(--unit-6-light); + --color-unit-6-dark: var(--unit-6-dark); --color-correct: var(--correct); --color-correct-light: var(--correct-light); diff --git a/app/lessons/unit-6-number-system/binary/page.tsx b/app/lessons/unit-6-number-system/binary/page.tsx new file mode 100644 index 0000000..8966e97 --- /dev/null +++ b/app/lessons/unit-6-number-system/binary/page.tsx @@ -0,0 +1,25 @@ +"use client"; + +import { Breadcrumbs } from "@/components/layout/breadcrumbs"; +import { BinaryConverterExplorer } from "@/components/explorers/binary-converter-explorer"; + +export default function BinaryPage() { + return ( +
+ + +
+

Binary → Decimal Converter

+

Same shape, different base — explore place value in powers of 2.

+
+ + +
+ ); +} diff --git a/app/lessons/unit-6-number-system/page.tsx b/app/lessons/unit-6-number-system/page.tsx new file mode 100644 index 0000000..6e47e0a --- /dev/null +++ b/app/lessons/unit-6-number-system/page.tsx @@ -0,0 +1,41 @@ +import Link from "next/link"; +import { curriculum } from "@/lib/curriculum"; +import { Card } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Breadcrumbs } from "@/components/layout/breadcrumbs"; + +export default function Unit6Overview() { + const unit = curriculum[5]; + + return ( +
+ +
+ Unit 6 — {unit.weeks} +

{unit.title}

+

{unit.description}

+
+
+ {unit.topics.map((topic, i) => ( + + +
+ + {i + 1} + + Week {topic.week} +
+

{topic.title}

+

{topic.description}

+
+ + ))} +
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index ee2c679..faff035 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -29,6 +29,11 @@ const unitStyles = { unitCard: "border-[#c4b5fd] bg-[#f5f0ff]", chip: "bg-[#6d28d9]", }, + "unit-6": { + tile: "from-[#818cf8] to-[#3730a3]", + unitCard: "border-[#a5b4fc] bg-[#eef2ff]", + chip: "bg-[#3730a3]", + }, }; const topicTiles = [ diff --git a/binary-to-decimal-converter.html b/binary-to-decimal-converter.html new file mode 100644 index 0000000..f71e2b0 --- /dev/null +++ b/binary-to-decimal-converter.html @@ -0,0 +1,459 @@ + + + + + + Binary to Decimal Converter - Form 1 + + + + +
+
+

BINARY → DECIMAL CONVERTER

+

Form 1 - Lesson 30 - Same shape, different base

+
+ +

Type a Binary Number

+
+ + + + + +
+ +
+ + + + + + + + +
+ + + +

Place Value Chart (powers of 2)

+
+ + +
+
+ +
+ + + +
+ + +
+ + +
+ + + + + diff --git a/components/explorers/binary-converter-explorer.tsx b/components/explorers/binary-converter-explorer.tsx new file mode 100644 index 0000000..a8e6d45 --- /dev/null +++ b/components/explorers/binary-converter-explorer.tsx @@ -0,0 +1,317 @@ +"use client"; + +import { useState, useMemo, useCallback } from "react"; +import { Card } from "@/components/ui/card"; + +const PRESETS = ["101", "1011", "1101", "10101", "11000", "11111", "100000", "110010"]; + +interface Column { + exponent: number; + placeValue: number; + digit: string; + contrib: number; + isOne: boolean; +} + +function powerOfTwo(n: number) { + let v = 1; + for (let i = 0; i < n; i++) v *= 2; + return v; +} + +function buildColumnsFor(binaryStr: string): Column[] { + const n = binaryStr.length; + const cols: Column[] = []; + for (let i = 0; i < n; i++) { + const exponent = n - 1 - i; + const digit = binaryStr[i]; + const placeValue = powerOfTwo(exponent); + const isOne = digit === "1"; + cols.push({ + exponent, + placeValue, + digit, + contrib: parseInt(digit, 10) * placeValue, + isOne, + }); + } + return cols; +} + +function emptyColumns(): Column[] { + const cols: Column[] = []; + for (let exp = 5; exp >= 0; exp--) { + cols.push({ + exponent: exp, + placeValue: powerOfTwo(exp), + digit: "_", + contrib: 0, + isOne: false, + }); + } + return cols; +} + +export function BinaryConverterExplorer() { + const [input, setInput] = useState(""); + const [submitted, setSubmitted] = useState(null); + const [error, setError] = useState(""); + const [answerHidden, setAnswerHidden] = useState(false); + const [chartHidden, setChartHidden] = useState(false); + + const handleInputChange = (value: string) => { + setInput(value.replace(/[^01]/g, "")); + setError(""); + }; + + const convert = useCallback((raw?: string) => { + const source = (raw ?? input).trim(); + if (source.length === 0) { + setError("Type a binary number first (only 0s and 1s)."); + setSubmitted(null); + return; + } + if (!/^[01]+$/.test(source)) { + setError("Binary numbers use only 0 and 1. Found another digit."); + setSubmitted(null); + return; + } + if (source.length > 8) { + setError("Keep it to 8 digits or fewer for this lesson."); + return; + } + setError(""); + setSubmitted(source); + }, [input]); + + const applyPreset = (value: string) => { + setInput(value); + convert(value); + }; + + const handleClear = () => { + setInput(""); + setSubmitted(null); + setError(""); + }; + + const handleRandom = () => { + const len = 3 + Math.floor(Math.random() * 4); + let s = "1"; + for (let i = 1; i < len; i++) { + s += Math.random() < 0.5 ? "0" : "1"; + } + setInput(s); + convert(s); + }; + + const columns = useMemo(() => { + if (!submitted) return emptyColumns(); + return buildColumnsFor(submitted); + }, [submitted]); + + const total = useMemo(() => { + if (!submitted) return 0; + return columns.reduce((acc, c) => acc + c.contrib, 0); + }, [columns, submitted]); + + const fullExpansion = submitted + ? columns.map((c) => `${c.digit}×${c.placeValue}`).join(" + ") + : ""; + + const onesOnly = submitted + ? columns.filter((c) => c.isOne).length > 0 + ? columns.filter((c) => c.isOne).map((c) => c.placeValue).join(" + ") + " = " + total + : "0" + : ""; + + return ( +
+ {/* Input Card */} + +

+ Type a Binary Number +

+
+ + handleInputChange(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") convert(); + }} + className="w-44 rounded-lg border-2 border-unit-6 bg-surface px-3 py-2 text-center font-mono text-2xl font-bold tracking-[0.3em] text-unit-6 outline-none focus:border-unit-6-dark" + aria-label="Binary number input" + /> + + + +
+ +
+ {PRESETS.map((p) => ( + + ))} +
+ + {error && ( +

+ {error} +

+ )} +
+ + {/* Place value chart */} + +
+

+ Place Value Chart (powers of 2) +

+ +
+ + {!chartHidden && ( +
+ + + + {columns.map((c) => ( + + ))} + + + {columns.map((c) => ( + + ))} + + + {columns.map((c, i) => ( + + ))} + + + {columns.map((c, i) => ( + + ))} + + +
+ 2{c.exponent} +
+ {c.placeValue} +
+ {c.digit} +
+ {submitted ? c.contrib : "_"} +
+
+ )} + + {submitted && ( +
+
{fullExpansion}
+
= {onesOnly}
+
+ )} +
+ + {/* Answer banner */} + + {!submitted ? ( +

+ Type a binary number above and press Convert. +

+ ) : answerHidden ? ( +

+ {submitted}2 = ? (press Show Answer) +

+ ) : ( +

+ {submitted}2 = {total} in denary +

+ )} + +
+ + {/* Footer note */} + +

+ For students: solve on paper first using the chart in your worksheet. Type the binary number here only to check. The lesson is in the chart, not the click. +

+
+
+ ); +} diff --git a/components/layout/mobile-nav.tsx b/components/layout/mobile-nav.tsx index d52854d..0073619 100644 --- a/components/layout/mobile-nav.tsx +++ b/components/layout/mobile-nav.tsx @@ -12,6 +12,7 @@ const unitColorMap = { "unit-3": "text-unit-3-dark", "unit-4": "text-unit-4-dark", "unit-5": "text-unit-5-dark", + "unit-6": "text-unit-6-dark", }; const unitDotColor = { @@ -20,6 +21,7 @@ const unitDotColor = { "unit-3": "bg-unit-3", "unit-4": "bg-unit-4", "unit-5": "bg-unit-5", + "unit-6": "bg-unit-6", }; export function MobileNav() { diff --git a/components/layout/sidebar.tsx b/components/layout/sidebar.tsx index 5d5c49b..a319e76 100644 --- a/components/layout/sidebar.tsx +++ b/components/layout/sidebar.tsx @@ -37,6 +37,12 @@ const unitColorMap = { heading: "text-unit-5-dark", hoverBg: "hover:bg-unit-5-light/50", }, + "unit-6": { + active: "bg-unit-6-light text-unit-6-dark border-unit-6/20", + dot: "bg-unit-6", + heading: "text-unit-6-dark", + hoverBg: "hover:bg-unit-6-light/50", + }, }; export function Sidebar() { diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index a88ec0c..f4152c4 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -1,7 +1,7 @@ import { cn } from "@/lib/utils"; import { type HTMLAttributes } from "react"; -type BadgeVariant = "default" | "unit-1" | "unit-2" | "unit-3" | "unit-4" | "unit-5"; +type BadgeVariant = "default" | "unit-1" | "unit-2" | "unit-3" | "unit-4" | "unit-5" | "unit-6"; interface BadgeProps extends HTMLAttributes { variant?: BadgeVariant; @@ -14,6 +14,7 @@ const variantStyles: Record = { "unit-3": "border border-unit-3/20 bg-unit-3-light text-unit-3-dark", "unit-4": "border border-unit-4/20 bg-unit-4-light text-unit-4-dark", "unit-5": "border border-unit-5/20 bg-unit-5-light text-unit-5-dark", + "unit-6": "border border-unit-6/20 bg-unit-6-light text-unit-6-dark", }; export function Badge({ variant = "default", className, children, ...props }: BadgeProps) { diff --git a/components/ui/card.tsx b/components/ui/card.tsx index 366faac..9848fe0 100644 --- a/components/ui/card.tsx +++ b/components/ui/card.tsx @@ -2,7 +2,7 @@ import { cn } from "@/lib/utils"; import { type HTMLAttributes } from "react"; interface CardProps extends HTMLAttributes { - accent?: "unit-1" | "unit-2" | "unit-3" | "unit-4" | "unit-5"; + accent?: "unit-1" | "unit-2" | "unit-3" | "unit-4" | "unit-5" | "unit-6"; hover?: boolean; } @@ -12,6 +12,7 @@ const accentStyles = { "unit-3": "border-l-4 border-l-unit-3", "unit-4": "border-l-4 border-l-unit-4", "unit-5": "border-l-4 border-l-unit-5", + "unit-6": "border-l-4 border-l-unit-6", }; export function Card({ diff --git a/lib/curriculum.ts b/lib/curriculum.ts index 84e1e2e..9349735 100644 --- a/lib/curriculum.ts +++ b/lib/curriculum.ts @@ -7,12 +7,12 @@ export interface Topic { } export interface Unit { - number: 1 | 2 | 3 | 4 | 5; + number: 1 | 2 | 3 | 4 | 5 | 6; slug: string; title: string; description: string; weeks: string; - color: "unit-1" | "unit-2" | "unit-3" | "unit-4" | "unit-5"; + color: "unit-1" | "unit-2" | "unit-3" | "unit-4" | "unit-5" | "unit-6"; topics: Topic[]; } @@ -200,6 +200,23 @@ export const curriculum: Unit[] = [ }, ], }, + { + number: 6, + slug: "unit-6-number-system", + title: "Number System", + description: "Explore how numbers can be written in different bases, beyond the everyday denary system", + weeks: "Week 10", + color: "unit-6", + topics: [ + { + slug: "binary", + title: "Binary Numbers", + shortTitle: "Binary", + week: 10, + description: "Place value in base 2 and converting binary numbers to denary (decimal)", + }, + ], + }, ]; export function getUnit(slug: string): Unit | undefined { @@ -211,13 +228,14 @@ export function getTopic(unitSlug: string, topicSlug: string): Topic | undefined return unit?.topics.find((t) => t.slug === topicSlug); } -export function getUnitColor(unitNumber: 1 | 2 | 3 | 4 | 5): string { +export function getUnitColor(unitNumber: 1 | 2 | 3 | 4 | 5 | 6): string { const colors = { 1: "unit-1", 2: "unit-2", 3: "unit-3", 4: "unit-4", 5: "unit-5", + 6: "unit-6", }; return colors[unitNumber]; }