You've already forked computing-box
7
src/components/Footer.astro
Normal file
7
src/components/Footer.astro
Normal file
@@ -0,0 +1,7 @@
|
||||
<footer class="siteFooter">
|
||||
<div class="siteFooter__inner">
|
||||
<div>Computer Science Concept Simulators</div>
|
||||
<div>© 2025 Computing:Box · Created with ❤️ by Mr Lyall</div>
|
||||
<div>Powered by ADCM Networks</div>
|
||||
</div>
|
||||
</footer>
|
||||
13
src/components/Header.astro
Normal file
13
src/components/Header.astro
Normal file
@@ -0,0 +1,13 @@
|
||||
<header class="siteHeader">
|
||||
<div class="siteHeader__inner">
|
||||
<a class="brand" href="/">Computing:Box</a>
|
||||
|
||||
<nav class="nav">
|
||||
<a class="nav__link" href="/binary">Binary</a>
|
||||
<a class="nav__link" href="/hexadecimal">Hexadecimal</a>
|
||||
<a class="nav__link" href="/hex-colours">Hex Colours</a>
|
||||
<a class="nav__link" href="/logic-gates">Logic Gates</a>
|
||||
<a class="nav__link" href="/about">About</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
@@ -1,6 +0,0 @@
|
||||
<footer class="site-footer">
|
||||
<div class="site-footer__inner">
|
||||
<div class="muted">© {new Date().getFullYear()} Computing:Box</div>
|
||||
<div class="muted">Powered by ADCM Networks</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -1,15 +0,0 @@
|
||||
<header class="site-header">
|
||||
<div class="site-header__inner">
|
||||
<a class="brand" href="/">
|
||||
<span class="brand__name">Computing:Box</span>
|
||||
</a>
|
||||
|
||||
<nav class="nav">
|
||||
<a href="/about/">About</a>
|
||||
<a href="/binary/">Binary</a>
|
||||
<a href="/hexadecimal/">Hexadecimal</a>
|
||||
<a href="/hex-colours/">Hex Colours</a>
|
||||
<a href="/logic-gates/">Logic Gates</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
@@ -1,9 +1,10 @@
|
||||
---
|
||||
import SiteHeader from "../components/SiteHeader.astro";
|
||||
import SiteFooter from "../components/SiteFooter.astro";
|
||||
import Header from "../components/Header.astro";
|
||||
import Footer from "../components/Footer.astro";
|
||||
|
||||
const { title = "Computing:Box" } = Astro.props;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -11,16 +12,18 @@ const { title = "Computing:Box" } = Astro.props;
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>{title}</title>
|
||||
|
||||
<!-- Global site styles -->
|
||||
<link rel="stylesheet" href="/styles/global.css" />
|
||||
</head>
|
||||
<body>
|
||||
<SiteHeader />
|
||||
<!-- Global site styling -->
|
||||
<link rel="stylesheet" href="/styles/site.css" />
|
||||
|
||||
<main class="site-main">
|
||||
<!-- Page specific styles can be added by each page -->
|
||||
<slot name="head" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Header />
|
||||
<main class="page">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,12 +3,11 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
---
|
||||
|
||||
<BaseLayout title="Binary | Computing:Box">
|
||||
<!-- Page-specific CSS -->
|
||||
<link rel="stylesheet" href="/styles/binary.css" />
|
||||
|
||||
<main class="wrap">
|
||||
<section class="topGrid">
|
||||
<!-- LEFT: readout + buttons + bits -->
|
||||
<section class="binaryWrap">
|
||||
<div class="topGrid">
|
||||
<!-- LEFT -->
|
||||
<div>
|
||||
<div class="readout">
|
||||
<div class="label">Denary</div>
|
||||
@@ -27,11 +26,11 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- Bits render here (JS builds bulbs + switches) -->
|
||||
<section class="bits" id="bitsGrid" aria-label="Bit switches"></section>
|
||||
<!-- Bit rows injected by JS -->
|
||||
<section id="bitsRows" class="bitsRows" aria-label="Bit switches"></section>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: mode + bit width -->
|
||||
<!-- RIGHT -->
|
||||
<aside class="panelCol">
|
||||
<div class="card">
|
||||
<div class="cardTitle">Mode</div>
|
||||
@@ -39,13 +38,12 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
<div class="toggleRow">
|
||||
<div class="toggleLabel" id="lblUnsigned">Unsigned</div>
|
||||
|
||||
<!-- SAME SWITCH STYLE as bit switches -->
|
||||
<label class="switch" aria-label="Toggle mode">
|
||||
<input id="modeToggle" type="checkbox" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
|
||||
<div class="toggleLabel" id="lblTwos">Two’s complement</div>
|
||||
<div class="toggleLabel" id="lblTwos">Two's complement</div>
|
||||
</div>
|
||||
|
||||
<div class="hint" id="modeHint">
|
||||
@@ -61,28 +59,17 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
|
||||
<div class="bitInputWrap">
|
||||
<div class="bitInputLabel">Bits</div>
|
||||
<input
|
||||
id="bitsInput"
|
||||
class="bitInput"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
min="4"
|
||||
max="64"
|
||||
step="1"
|
||||
value="8"
|
||||
aria-label="Number of bits"
|
||||
/>
|
||||
<input id="bitsInput" class="bitInput" type="number" inputmode="numeric" min="4" max="64" step="1" value="8" />
|
||||
</div>
|
||||
|
||||
<button class="miniBtn" id="btnBitsUp" type="button" aria-label="Increase bits">+</button>
|
||||
</div>
|
||||
|
||||
<div class="hint">Minimum 4 bits, maximum 64 bits. Display wraps every 8 bits.</div>
|
||||
<div class="hint">Minimum 4 bits, maximum 64 bits.</div>
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Page-specific JS -->
|
||||
<script type="module" src="/scripts/binary.js"></script>
|
||||
</BaseLayout>
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
let bits = [128,64,32,16,8,4,2,1];
|
||||
let state = Array(8).fill(0);
|
||||
|
||||
const denaryEl = document.getElementById("denaryNumber");
|
||||
const binaryEl = document.getElementById("binaryNumber");
|
||||
const bitEls = document.querySelectorAll(".bit");
|
||||
|
||||
bitEls.forEach((el, i) => {
|
||||
el.addEventListener("click", () => {
|
||||
state[i] = state[i] ? 0 : 1;
|
||||
el.classList.toggle("on");
|
||||
update();
|
||||
});
|
||||
});
|
||||
|
||||
function update() {
|
||||
const denary = state.reduce((sum, bit, i) => sum + bit * bits[i], 0);
|
||||
denaryEl.textContent = denary;
|
||||
binaryEl.textContent = state.join("");
|
||||
}
|
||||
|
||||
function requestBinary() {
|
||||
const input = prompt("Enter 8-bit binary:");
|
||||
if (!/^[01]{8}$/.test(input)) return;
|
||||
state = input.split("").map(Number);
|
||||
bitEls.forEach((el,i)=>el.classList.toggle("on",state[i]));
|
||||
update();
|
||||
}
|
||||
|
||||
function requestDenary() {
|
||||
const input = parseInt(prompt("Enter denary (0–255)"),10);
|
||||
if (isNaN(input) || input < 0 || input > 255) return;
|
||||
|
||||
let value = input;
|
||||
state = bits.map(b => {
|
||||
if (value >= b) {
|
||||
value -= b;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
bitEls.forEach((el,i)=>el.classList.toggle("on",state[i]));
|
||||
update();
|
||||
}
|
||||
|
||||
function shiftBinary(dir) {
|
||||
if (dir === "left") state.shift(), state.push(0);
|
||||
if (dir === "right") state.pop(), state.unshift(0);
|
||||
bitEls.forEach((el,i)=>el.classList.toggle("on",state[i]));
|
||||
update();
|
||||
}
|
||||
|
||||
update();
|
||||
@@ -1,52 +0,0 @@
|
||||
src/styles/base.css
|
||||
@import "./md3-tokens.css";
|
||||
html, body{ height:100%; }
|
||||
body{
|
||||
margin:0;
|
||||
font-family: var(--font-sans);
|
||||
background: var(--md-surface-2);
|
||||
color: var(--md-on-surface);
|
||||
}
|
||||
a{ color: var(--md-primary); text-decoration: none; }
|
||||
a:hover{ text-decoration: underline; }
|
||||
.container{
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 16px;
|
||||
}
|
||||
.card{
|
||||
background: var(--md-surface);
|
||||
border: 1px solid var(--md-outline);
|
||||
border-radius: var(--radius-2);
|
||||
box-shadow: var(--shadow-1);
|
||||
padding: 16px;
|
||||
}
|
||||
.btn{
|
||||
display:inline-flex;
|
||||
gap:8px;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--md-outline);
|
||||
background: var(--md-surface);
|
||||
color: var(--md-on-surface);
|
||||
padding: 10px 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn:hover{ filter: brightness(0.98); }
|
||||
.btn:focus{ outline:none; box-shadow: var(--md-focus); }
|
||||
.btn-primary{
|
||||
background: var(--md-primary);
|
||||
color: var(--md-on-primary);
|
||||
border-color: transparent;
|
||||
}
|
||||
.badge{
|
||||
display:inline-block;
|
||||
padding: 2px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
border: 1px solid var(--md-outline);
|
||||
background: var(--md-surface-2);
|
||||
}
|
||||
code, pre{ font-family: var(--font-mono); }
|
||||
@@ -1,68 +0,0 @@
|
||||
.binary-container {
|
||||
max-width: 1100px;
|
||||
margin: auto;
|
||||
padding: 2rem;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.display {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #00ff66;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.controls button {
|
||||
margin: 0.25rem;
|
||||
padding: 0.6rem 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.bits {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
gap: 1rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.bit {
|
||||
width: 40px;
|
||||
height: 80px;
|
||||
background: #333;
|
||||
border-radius: 20px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bit::after {
|
||||
content: "";
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: #555;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
left: 4px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.bit.on {
|
||||
background: #00c853;
|
||||
}
|
||||
|
||||
.bit.on::after {
|
||||
bottom: 42px;
|
||||
background: #eaffea;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "DSEG7";
|
||||
src: url("/fonts/DSEG7Classic-Regular.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.dseg {
|
||||
font-family: "DSEG7", monospace;
|
||||
letter-spacing: 0.15em;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
src/styles/md3-tokens.css
|
||||
/* MD3-inspired tokens tuned for education: high readability, clear contrast, calm surfaces */
|
||||
:root{
|
||||
/* Typography */
|
||||
--font-sans: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
|
||||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
|
||||
/* Spacing + shape */
|
||||
--radius-1: 10px;
|
||||
--radius-2: 16px;
|
||||
--radius-3: 22px;
|
||||
--shadow-1: 0 1px 2px rgba(0,0,0,.08), 0 2px 8px rgba(0,0,0,.06);
|
||||
/* Color roles (keep simple) */
|
||||
--md-surface: #ffffff;
|
||||
--md-surface-2: #f6f7fb;
|
||||
--md-on-surface: #111318;
|
||||
--md-primary: #2f6fed; /* calm blue */
|
||||
--md-on-primary: #ffffff;
|
||||
--md-secondary: #5a5f72; /* muted */
|
||||
--md-on-secondary: #ffffff;
|
||||
--md-tertiary: #0f766e; /* teal for "practical" tools */
|
||||
--md-on-tertiary: #ffffff;
|
||||
--md-outline: #d7dbe7;
|
||||
--md-success: #1a7f37;
|
||||
--md-warning: #b54708;
|
||||
--md-danger: #b42318;
|
||||
/* Focus ring for accessibility */
|
||||
--md-focus: 0 0 0 3px rgba(47,111,237,.28);
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
:root{
|
||||
--md-surface: #0b0e14;
|
||||
--md-surface-2: #121725;
|
||||
--md-on-surface: #e8eaf2;
|
||||
--md-primary: #9bb6ff;
|
||||
--md-on-primary: #0b0e14;
|
||||
--md-secondary: #b8bccd;
|
||||
--md-on-secondary: #0b0e14;
|
||||
--md-tertiary: #4fd1c5;
|
||||
--md-on-tertiary: #0b0e14;
|
||||
--md-outline: #2b3244;
|
||||
--md-focus: 0 0 0 3px rgba(155,182,255,.25);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user