diff --git a/website/docs/getting-started/my-first-diamond.mdx b/website/docs/getting-started/my-first-diamond.mdx
new file mode 100644
index 00000000..23d7884a
--- /dev/null
+++ b/website/docs/getting-started/my-first-diamond.mdx
@@ -0,0 +1,313 @@
+---
+sidebar_position: 2
+title: "Building Your First Diamond"
+sidebar_label: "Build Your First Diamond"
+description: "Follow along to scaffold, build, test, and deploy your first diamond smart contract application with Compose"
+---
+
+import DocSubtitle from '@site/src/components/docs/DocSubtitle';
+import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard';
+import Callout from '@site/src/components/ui/Callout';
+import ExpandableCode from '@site/src/components/code/ExpandableCode';
+
+# Building Your First Diamond
+
+
+Create a working diamond project using Compose then explore every file so you know exactly what you have.
+
+
+This tutorial uses the `Counter` example: a minimal diamond with increment and read functions. It's the simplest way to see how diamonds works end-to-end.
+
+## Prerequisites
+
+- [Node.js](https://nodejs.org/) >= 20
+- [Foundry](https://book.getfoundry.sh/getting-started/installation) installed and on your PATH
+
+
+Althought the CLI support both Foundry and Hardhat, this tutorial will focus on a Foundry based project. All learnings from the diamond archtiecture are the same for both framework
+
+If you haven't used Foundry before, follow the official installation guide first. You only need `forge` and `anvil` for this tutorial.
+
+
+## Step 1) Install the CLI
+
+Install the Compose CLI so you can run all the CLI commands
+
+
+{`npm install -g @perfect-abstractions/compose-cli`}
+
+
+Verify it's installed:
+
+
+{`compose --help`}
+
+
+You can skip the global package installation. Just use `npx @perfect-abstractions/compose-cli` wherever you see `compose` in this tutorial.
+
+## Step 2) Scaffold the project
+
+
+{`compose init`}
+
+
+You'll be prompt to choose from a lot of different options from framework, ERC standards templates, and others.
+
+For this tutorial, we will choose the `Counter` example with the following options:
+
+
+{`Enter project name: my-diamond
+Select project framework: Foundry
+Select base: Counter
+Select extension facets: None
+Select Compose library facets: None
+Select ownership: None
+Select access control: None
+
+? Install project dependencies? (Y/n) Yes`}
+
+
+Take the time to review the full library available to you. We support most major token standards like `ERC20`, `ERC721` with different type of access control.
+
+You can always skip the interactive prompts entirely:
+
+{`compose init my-first-diamond --framework foundry --base counter --yes`}
+
+
+
+When the CLI finishes, follow the next steps instruction to move into your project and test it:
+
+
+{`✔ Project "my-first-diamond" scaffolded in ".../my-first-diamond"
+
+Next steps:
+1. cd my-first-diamond
+2. forge build && forge test`}
+
+
+## Step 3) Explore the project structure
+
+The scaffold generates a diamond with two local facets and one from the Compose library.
+
+
+{`src/
+ Diamond.sol ← proxy: wires everything together
+ facets/
+ CounterDataFacet.sol ← get the current count
+ CounterIncrementFacet.sol ← increment the counter`}
+
+
+Each facet owns a slice of the logic. `CounterDataFacet` exposes the count, `CounterIncrementFacet` mutates it. The `Diamond.sol` proxy routes calls to the right facet via function selectors.
+
+### Diamond.sol: The proxy
+
+The diamond is the application entrypoint. Users call it, but it holds no logic itself, only the storage state. Every call is delegated to a stateless facet via the fallback function:
+
+
+{`contract Diamond {
+ constructor(address[] memory _facets) {
+ // Add all the facets address provided to constructor
+ DiamondMod.addFacets(_facets);
+ }
+
+ fallback() external payable {
+ DiamondMod.diamondFallback();
+ }
+
+ receive() external payable {}
+}`}
+
+
+When a function is called on the diamond, [`DiamondMod.diamondFallback()`](/docs/library/diamond/DiamondMod#diamondfallback) looks up which contract owns that selector and forwards the call.
+
+There nothing more to make a diamond proxy work. Now, let's transition where our application logic lives
+
+### CounterDataFacet.sol
+
+Facets need to share state. They do this by pointing to the same storage slot:
+
+
+{`contract CounterDataFacet {
+ // 1. Slot address: a deterministic location shared across the diamond
+ bytes32 constant STORAGE_POSITION = keccak256("counter");
+
+ // 2. Data struct: defines the shape of data stored at that slot:
+ struct CounterStorage {
+ uint256 count;
+ }
+
+ // 3. Assembly storage getter: \`storage\` reference pointer to that slot:
+ function getStorage() internal pure returns (CounterStorage storage s) {
+ bytes32 position = STORAGE_POSITION;
+ assembly {
+ s.slot := position
+ }
+ }
+
+ // ----------------------------------
+ // Facet functions
+ // ----------------------------------
+
+ function getCount() external view returns (uint256) {
+ return getStorage().count;
+ }
+
+ // Declares which selectors this facet registers on the diamond proxy
+ function exportSelectors() external pure returns (bytes memory) {
+ return bytes.concat(this.getCount.selector);
+ }
+}`}
+
+
+Any facet that need `Counter` data uses the same `STORAGE_POSITION [ keccak256("counter") ]` and `CounterStorage` struct. They never import each other. They just agree on the same storage location.
+
+Facets can be scoped based on your project needs. Compose encourages small scoped facets, one facet per responsibility, to allow granular composition. In this example, `CounterDataFacet` handles reads and `CounterIncrementFacet` handles mutations. You could combine them into a single facet, but keeping them separate lets you pick exactly the functionality you need at anytime
+
+## Step 4) Build and test
+
+Now let's compile and run the tests:
+
+
+{`forge build && forge test -vv`}
+
+
+You should see compilation succeed and one passing test in `/test/Diamond.t.sol`
+
+The test deploys a diamond with all three facets the `Counter` needs, then calls `facetAddresses()` on `DiamondInspectFacet` to verify the right number of facets are registered.
+
+## Step 5) Write your own test
+
+Let's interact with the `Counter` through a test. Open `test/Diamond.t.sol` and take a look at what's already there:
+
+
+{`// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.30;
+
+import {Test} from "forge-std/Test.sol";
+import {Diamond} from "../src/Diamond.sol";
+import {CounterDataFacet} from "../src/facets/CounterDataFacet.sol";
+import {CounterIncrementFacet} from "../src/facets/CounterIncrementFacet.sol";
+import {DiamondInspectFacet} from "@perfect-abstractions/compose/diamond/DiamondInspectFacet.sol";
+
+contract DiamondTest is Test {
+ Diamond diamond;
+
+ // This setup function runs before each test
+ function setUp() public {
+ address[] memory facets = new address[](3);
+
+ // 1. Deploying the Counter specific facets
+ facets[0] = address(new CounterDataFacet());
+ facets[1] = address(new CounterIncrementFacet());
+
+ // 2. Deploying the required Inspection Facet from @perfect-abstractions/compose
+ facets[2] = address(new DiamondInspectFacet());
+
+ // 3. Deploying your proxy with all 3 addresses passed to the constructor
+ diamond = new Diamond(facets);
+ }
+
+ function test_inspect_facetAddresses() public view {
+ DiamondInspectFacet inspect = DiamondInspectFacet(address(diamond));
+ address[] memory addresses = inspect.facetAddresses();
+ assertEq(addresses.length, 3);
+ }
+}`}
+
+
+The test file already has a `setUp()` function that deploys a fresh `Diamond` instance before each test. There's also one test that verifies all three facets are properly registered on the proxy.
+
+Take a look at the order here. The facets are deployed first as separate contracts, then the Diamond proxy is created with references to those pre-deployed facet addresses. This is the core of [composition](/docs/design/design-for-composition) over inheritance. Instead of a monolithic contract that `is` everything through inheritance chains, the Diamond `has` the facets it needs. They're composed at runtime via the constructor or the upgrade functionality.
+
+Now let's add a test that actually uses our `Counter`. Add this function inside the `DiamondTest` contract:
+
+
+{`function test_counter_incrementAndRead() public {
+ // Unique address entrypoint
+ address diamondAddress = address(diamond);
+
+ // Cast diamond to both facet interfaces
+ CounterIncrementFacet incrementer = CounterIncrementFacet(diamondAddress);
+ CounterDataFacet getter = CounterDataFacet(diamondAddress);
+
+ // Initial count should be 0
+ assertEq(getter.getCount(), 0);
+
+ // Increment through one facet
+ incrementer.increment();
+
+ // Counter should now be 1
+ assertEq(getter.getCount(), 1);
+
+ // Increment again by 2 (counter should now be 3)
+ incrementer.incrementBy(2);
+ assertEq(getter.getCount(), 3);
+}
+`}
+
+
+Run the test suite:
+
+
+{`forge test`}
+
+
+All tests pass, demonstrating that data written by one facet is immediately visible to another through the diamond's shared storage.
+
+## Step 6) Deploy locally
+
+Start a local Ethereum node in one terminal:
+
+
+{`anvil`}
+
+
+In a second terminal, deploy your Counter application:
+
+
+{`forge script script/Deploy.s.sol:DeployScript \\
+ --rpc-url http://localhost:8545 \\
+ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\
+ --broadcast`}
+
+
+
+The private key above is Anvil's default test key. **Never use it on a real network.**
+
+
+You'll see the deployed addresses logged:
+
+
+{`== Logs ==
+ CounterDataFacet: 0x5FbDB2315678afecb367f032d93F642f64180aa3
+ CounterIncrementFacet: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
+ DiamondInspectFacet: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
+ Diamond: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9`}
+
+
+#### Congratulations!
+
+You just deployed your first diamond proxy application. You've scaffolded a `Counter` project using the CLI, written tests that verify shared storage across facets, and deployed a composed system to a local network.
+
+We recommend you to explore all the template available to scaffold. Use the the `catalog` command to review what's available to you.
+
+## What's next?
+
+
+
+
+
+
+
+Run `npx @perfect-abstractions/compose-cli init` again and pick **ERC-20** or **ERC-721** to see how the same pattern scales to token standards.
+
diff --git a/website/src/css/code-blocks.css b/website/src/css/code-blocks.css
index ae6b8885..c8adc663 100644
--- a/website/src/css/code-blocks.css
+++ b/website/src/css/code-blocks.css
@@ -144,7 +144,11 @@ code {
}
.token.comment {
- color: #546e7a;
- font-style: italic;
+ color: #425c68 !important;
+}
+
+/* Dark mode - lighter comment color for readability on dark background */
+[data-theme='dark'] .token.comment {
+ color: #b0b9c7 !important;
}
diff --git a/website/src/css/sidebar.css b/website/src/css/sidebar.css
index fd674079..c7574bda 100644
--- a/website/src/css/sidebar.css
+++ b/website/src/css/sidebar.css
@@ -21,12 +21,12 @@
/* Dark mode sidebar */
[data-theme='dark'] .theme-doc-sidebar-container {
- background-color: #0f172a;
- border-right: 1px solid #1e293b;
+ background-color: #020613;
+ border-right: 1px solid #020613;
}
[data-theme='dark'] .menu {
- background-color: #0f172a;
+ background-color: #020613;
}
[data-theme='dark'] .theme-doc-toc-desktop {
diff --git a/website/src/css/variables.css b/website/src/css/variables.css
index 1d64ac53..8d9343fe 100644
--- a/website/src/css/variables.css
+++ b/website/src/css/variables.css
@@ -110,9 +110,9 @@
--ifm-color-accent-light: #7dd3fc;
/* Dark blue backgrounds */
- --ifm-background-color: #0f172a;
- --ifm-background-surface-color: #1e293b;
- --ifm-navbar-background-color: #0a0e1a;
+ --ifm-background-color: #020613;
+ --ifm-background-surface-color: #020613;
+ --ifm-navbar-background-color: #050b1d;
/* Sidebar colors */
--ifm-menu-color: #a0a0a0;
@@ -131,30 +131,30 @@
--ifm-color-emphasis-900: #ffffff;
/* Border colors - blue tones */
- --ifm-color-emphasis-100: #1e293b;
- --ifm-color-emphasis-200: #334155;
+ --ifm-color-emphasis-100: #020613;
+ --ifm-color-emphasis-200: #020613;
- --ifm-code-background: #1e293b;
+ --ifm-code-background: #020613;
--docusaurus-highlighted-code-line-bg: rgba(59, 130, 246, 0.15);
- /* Brand tokens (dark) */
- --compose-bg-900: #0f172a;
- --compose-bg-800: #1e293b;
+/* Brand tokens (dark) */
+ --compose-bg-900: #020613;
+ --compose-bg-800: #020613;
--compose-primary-500: #60a5fa;
--compose-primary-600: #3b82f6;
/* Homepage CTA + stats — reversed gradients meet at same hue (mirrors light band) */
- --home-band-bg: linear-gradient(180deg, var(--compose-bg-900) 0%, #0c1323 100%);
- /* CTA bottom (#0c1323) must equal stats top — same as light reverse-bg, not duplicate of --home-band-bg */
- --home-band-reverse-bg: linear-gradient(180deg, #0c1323 0%, var(--compose-bg-900) 100%);
+ --home-band-bg: linear-gradient(180deg, var(--compose-bg-900) 0%, #020613 100%);
+ /* CTA bottom (#020613) must equal stats top — same as light reverse-bg, not duplicate of --home-band-bg */
+ --home-band-reverse-bg: linear-gradient(180deg, #020613 0%, var(--compose-bg-900) 100%);
/* Hero gradient - dark blue */
--hero-gradient: linear-gradient(135deg, var(--compose-bg-900) 0%, var(--compose-bg-800) 100%);
- --hero-gradient-alt: linear-gradient(135deg, var(--compose-bg-800) 0%, #334155 100%);
+ --hero-gradient-alt: linear-gradient(135deg, var(--compose-bg-800) 0%, #020613 100%);
/* Homepage hero tokens (dark overrides if needed) */
- --hero-bg-start: #0f172a;
- --hero-bg-end: #1e293b;
+ --hero-bg-start: #020613;
+ --hero-bg-end: #020613;
--hero-text-strong: rgba(255, 255, 255, 1);
--hero-text-medium: rgba(255, 255, 255, 0.9);
--hero-text-weak: rgba(255, 255, 255, 0.75);
@@ -182,15 +182,15 @@ html {
/* Main content area dark styling */
[data-theme='dark'] .main-wrapper {
- background: #0f172a;
+ background: #020613;
}
[data-theme='dark'] .docMainContainer {
- background: #0f172a;
+ background: #020613;
}
[data-theme='dark'] article {
- background: #0f172a;
+ background: #020613;
}
/* Documentation content wrapper */