summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2025-05-21 17:59:44 +0200
committerYuval Adam <_@yuv.al>2025-05-21 17:59:44 +0200
commitf043c0bae4242f117bf3ea545228709e11db6a2e (patch)
treeb761d07e5559650f90116dba73bcedf93ed6cc76
parent10a5f6c5d9280bd375db64f6581d9a554ce2c496 (diff)
Initial guide
-rw-r--r--package-lock.json62
-rw-r--r--package.json5
-rw-r--r--src/components/Prose.astro27
-rw-r--r--src/content/cidr-guide.md74
-rw-r--r--src/pages/cidr-calculator-subnet-guide.astro113
-rw-r--r--src/styles/global.css3
6 files changed, 280 insertions, 4 deletions
diff --git a/package-lock.json b/package-lock.json
index 44fc123..66398f3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
"dependencies": {
"@astrojs/react": "^4.2.7",
"@tailwindcss/vite": "^4.1.5",
+ "@types/netmask": "^2.0.5",
"@types/react": "^19.1.3",
"@types/react-dom": "^19.1.3",
"astro": "^5.7.12",
@@ -19,7 +20,7 @@
"tailwindcss": "^4.1.5"
},
"devDependencies": {
- "@types/netmask": "^2.0.5"
+ "@tailwindcss/typography": "^0.5.16"
}
},
"node_modules/@ampproject/remapping": {
@@ -1840,6 +1841,22 @@
"node": ">= 10"
}
},
+ "node_modules/@tailwindcss/typography": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
+ "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash.castarray": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.merge": "^4.6.2",
+ "postcss-selector-parser": "6.0.10"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
+ }
+ },
"node_modules/@tailwindcss/vite": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz",
@@ -1947,7 +1964,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/netmask/-/netmask-2.0.5.tgz",
"integrity": "sha512-9Q5iw9+pHZBVLDG700dlQSWWHTYvOb8KfPjfQTNckCYky4IyjV2xh81+RgC1CCwqv92bYLpz1cVKyJav0B88uQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/@types/nlcst": {
@@ -3627,6 +3643,27 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/lodash.castarray": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
+ "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/longest-streak": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@@ -4782,6 +4819,20 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.10",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+ "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/prismjs": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
@@ -5792,6 +5843,13 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/vfile": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
diff --git a/package.json b/package.json
index e5540fb..47be3c8 100644
--- a/package.json
+++ b/package.json
@@ -20,5 +20,8 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tailwindcss": "^4.1.5"
+ },
+ "devDependencies": {
+ "@tailwindcss/typography": "^0.5.16"
}
-} \ No newline at end of file
+}
diff --git a/src/components/Prose.astro b/src/components/Prose.astro
new file mode 100644
index 0000000..aecfca2
--- /dev/null
+++ b/src/components/Prose.astro
@@ -0,0 +1,27 @@
+---
+// Prose component for styling markdown content
+---
+
+<div
+ class="prose prose-lg max-w-none mx-auto px-4 sm:px-6 lg:px-8 py-8
+ prose-headings:font-bold prose-headings:text-gray-900 prose-headings:mb-4 prose-headings:mt-8
+ prose-h1:text-4xl prose-h1:mt-0
+ prose-h2:text-2xl prose-h2:border-b prose-h2:border-gray-200 prose-h2:pb-2
+ prose-h3:text-xl
+ prose-p:text-gray-700 prose-p:my-4 prose-p:leading-relaxed
+ prose-a:text-blue-600 prose-a:font-medium prose-a:no-underline hover:prose-a:underline
+ prose-strong:font-bold prose-strong:text-gray-900
+ prose-ul:my-6 prose-ul:list-disc prose-ul:pl-6
+ prose-ol:my-6 prose-ol:list-decimal prose-ol:pl-6
+ prose-li:my-2 prose-li:text-gray-700
+ prose-blockquote:border-l-4 prose-blockquote:border-blue-500 prose-blockquote:pl-4 prose-blockquote:py-1 prose-blockquote:my-6 prose-blockquote:bg-blue-50 prose-blockquote:rounded-r-md
+ prose-blockquote:text-gray-700 prose-blockquote:italic
+ prose-img:rounded-lg prose-img:shadow-md prose-img:my-8
+ prose-hr:my-8 prose-hr:border-gray-200
+ prose-code:text-gray-800 prose-code:bg-gray-100 prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:text-sm
+ prose-pre:bg-gray-900 prose-pre:text-gray-100 prose-pre:p-4 prose-pre:rounded-lg prose-pre:overflow-x-auto prose-pre:my-6
+ prose-pre:shadow-md prose-pre:text-sm
+ dark:prose-invert dark:prose-pre:bg-gray-800 dark:prose-code:bg-gray-800 dark:prose-blockquote:bg-gray-800"
+>
+ <slot />
+</div>
diff --git a/src/content/cidr-guide.md b/src/content/cidr-guide.md
new file mode 100644
index 0000000..38fe8b2
--- /dev/null
+++ b/src/content/cidr-guide.md
@@ -0,0 +1,74 @@
+# CIDR Calculator & Subnet Guide
+
+Convert CIDR blocks to IP ranges and master subnetting for IPv4 & IPv6—all in one place.
+
+---
+
+## What is CIDR?
+
+**Classless Inter-Domain Routing (CIDR)** is the system that replaced the old class-A/B/C scheme in 1993.
+A network is written as
+
+```
+address/prefix-length → 10.0.0.0/24
+```
+
+The *prefix length* (the number after the slash) tells routers how many of the 32 bits belong to the network portion:
+
+* `10.0.0.0/24`  → 24 network bits + 8 host bits → **256** addresses (254 usable)
+* `10.0.0.0/30`  → 30 network bits + 2 host bits → **4** addresses (2 usable)
+
+CIDR lets ISPs advertise aggregated “supernets” such as `203.0.113.0/22` instead of listing a thousand individual /24 routes—shrinking global routing tables and conserving address space.
+
+---
+
+## CIDR → IP Range Converter
+
+Paste any block—say `192.168.15.64/27`—into the calculator. Behind the scenes it:
+
+1. **Bitwise-ANDs** the IP with the mask to find the **network address**.
+2. **Inverts** the mask and ORs it with the IP to get the **broadcast address**.
+3. Offsets ±1 to reveal the **first** and **last usable hosts**.
+
+Hit **Copy Share Link** to bookmark or send an exact result.
+
+---
+
+## Subnetting Tutorial — Quick Steps
+
+Subnetting seems daunting until you break it into four predictable moves:
+
+1. **Convert the prefix** `/26` → `255 .255 .255 .192`
+2. **Find block size** 256 − 192 = 64
+3. **List subnet starts** 0, 64, 128, 192
+4. **Pick your block** Usable hosts = block size − 2 (62 in a /26)
+
+> **Example**
+> Split `172.16.0.0/16` into at least 10 equal subnets.
+> *Need ≥10 blocks → 4 extra bits (2⁴ = 16)*
+> New prefix = **/20**. Each /20 holds 4094 hosts.
+> Ranges begin 172.16.0.0, 172.16.16.0, 172.16.32.0 … to 172.16.240.0.
+
+---
+
+## IPv6 Subnetting Basics
+
+The slash notation is identical in IPv6—just with 128-bit addresses.
+
+* **/64**  is the standard LAN size → 18 quintillion addresses
+* Providers often delegate:
+
+ * `/48`  — 65 536 customer /64s
+ * `/56`  — 256 /64s
+ * `/60`  — 16 /64s
+
+Toggle **IPv6** in the calculator and try `2001:db8::/48` to explore the range graphically.
+
+---
+
+## Further Reading & Tools
+
+* RFC 4632 — *Classless Inter-Domain Routing for IPv6*
+* RFC 1519 — *CIDR Address Allocation for IPv4*
+* [cidr.xyz source code](https://github.com/yuvadm/cidr.xyz)
+* *Subnetting Examples* blog series (coming soon)
diff --git a/src/pages/cidr-calculator-subnet-guide.astro b/src/pages/cidr-calculator-subnet-guide.astro
new file mode 100644
index 0000000..09b553a
--- /dev/null
+++ b/src/pages/cidr-calculator-subnet-guide.astro
@@ -0,0 +1,113 @@
+---
+import Layout from "../layouts/Layout.astro";
+import Cidr from "../components/Cidr";
+import { faqs } from "../content/faqs.ts";
+import Prose from "../components/Prose.astro";
+
+// ────────────────────────────────────────────────────────────
+// Metadata
+// ────────────────────────────────────────────────────────────
+const title = "CIDR Calculator & Subnet Guide — IP Ranges, Masks & IPv6";
+const description =
+ "Free interactive CIDR calculator and comprehensive plain‑language guide covering subnetting, IP ranges, subnet masks and IPv6 addressing.";
+
+// FAQ structured‑data (kept in sync with UI)
+const faqJsonLd = {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ mainEntity: faqs.map(({ q, a }) => ({
+ "@type": "Question",
+ name: q,
+ acceptedAnswer: { "@type": "Answer", text: a },
+ })),
+};
+
+import { Content as GuideContent } from "../content/cidr-guide.md";
+---
+
+<Layout title={title} description={description}>
+ {
+ /**
+ ────────────────────────────────────────────────────────────
+ Hero
+ ────────────────────────────────────────────────────────────*/
+ }
+ <header
+ class="relative isolate overflow-hidden bg-gradient-to-br from-blue-50 via-indigo-50 to-white rounded-xl shadow-sm"
+ >
+ <div class="mx-auto max-w-7xl px-4 py-20 sm:py-24 lg:px-6 text-center">
+ <h1
+ class="text-4xl font-extrabold tracking-tight text-gray-900 sm:text-6xl"
+ >
+ CIDR Calculator &amp; Subnet Guide
+ </h1>
+ <p class="mt-4 text-lg sm:text-xl max-w-2xl mx-auto text-gray-700">
+ Convert CIDR blocks to IP ranges and master subnetting for IPv4
+ &amp; IPv6 — in one streamlined resource.
+ </p>
+ </div>
+ </header>
+
+ {
+ /**
+ ────────────────────────────────────────────────────────────
+ Calculator block
+ ────────────────────────────────────────────────────────────*/
+ }
+ <section class="mx-auto mt-12 max-w-4xl px-2 sm:px-4">
+ <Cidr client:load />
+ </section>
+
+ {
+ /**
+ ────────────────────────────────────────────────────────────
+ Table of Contents (desktop only for cleanliness)
+ ────────────────────────────────────────────────────────────*/
+ }
+ <nav
+ aria-label="Page sections"
+ class="hidden lg:block sticky top-0 z-10 bg-white/90 backdrop-blur border-b border-gray-200"
+ >
+ <div class="mx-auto max-w-4xl px-4">
+ <ul
+ class="flex flex-wrap justify-center gap-6 py-3 text-sm font-medium text-gray-600"
+ >
+ <li>
+ <a href="#what-is-cidr" class="hover:text-blue-600"
+ >What is CIDR?</a
+ >
+ </li>
+ <li>
+ <a href="#cidr-to-ip" class="hover:text-blue-600"
+ >CIDR → IP range</a
+ >
+ </li>
+ <li>
+ <a href="#subnetting-tutorial" class="hover:text-blue-600"
+ >Subnetting tutorial</a
+ >
+ </li>
+ <li>
+ <a href="#ipv6" class="hover:text-blue-600">IPv6 basics</a>
+ </li>
+ <li><a href="#faq" class="hover:text-blue-600">FAQ</a></li>
+ <li>
+ <a href="#resources" class="hover:text-blue-600"
+ >Resources</a
+ >
+ </li>
+ </ul>
+ </div>
+ </nav>
+
+ <Prose>
+ <GuideContent />
+ </Prose>
+
+ {/** Structured‑data block */}
+ <script
+ type="application/ld+json"
+ is:inline
+ set:html={JSON.stringify(faqJsonLd, null, 2)}
+ />
+</Layout>
diff --git a/src/styles/global.css b/src/styles/global.css
index a461c50..cba2dd6 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -1 +1,2 @@
-@import "tailwindcss"; \ No newline at end of file
+@import "tailwindcss";
+@plugin '@tailwindcss/typography'; \ No newline at end of file