This site documents pre-release features of CustardUI and possible beta breaking changes. For the latest stable release, visit here.
CustardUI
Let readers customise your static web pages!

Add CustardUI to any static site and let readers personalise their experience. Hide sections, set preferences and share exactly the right content with others. No backend required.

Read the docs    •    View on GitHub    •    npm package


What it can do

Hide irrelevant sections; Collapse less relevant sections.

Let readers collapse what they don't need. Their choice sticks across pages and sessions.

https://project-documentation.org/best-practices

Dependency Injection

Toggle:

Dependency Injection (DI) is a technique where an object's dependencies are provided externally rather than created by the object itself. This reduces coupling and makes code easier to test.

// Without DI — tightly coupled
class UserService {
    private Database db = new Database();
}

// With DI — dependency is injected
class UserService {
    private Database db;

    public UserService(Database db) {
        this.db = db;
    }
}

Prefer constructor injection over field injection — it makes dependencies explicit and the class easier to test.

Dependency Injection

Toggle:

Dependency Injection (DI) is a technique where an object's dependencies are provided externally rather than created by the object itself. This reduces coupling and makes code easier to test.

// Without DI — tightly coupled
class UserService {
    private Database db = new Database();
}

// With DI — dependency is injected
class UserService {
    private Database db;

    public UserService(Database db) {
        this.db = db;
    }
}

Prefer constructor injection over field injection — it makes dependencies explicit and the class easier to test.

Dependency Injection

Toggle:

Dependency Injection (DI) is a technique where an object's dependencies are provided externally rather than created by the object itself. This reduces coupling and makes code easier to test.

// Without DI — tightly coupled
class UserService {
    private Database db = new Database();
}

// With DI — dependency is injected
class UserService {
    private Database db;

    public UserService(Database db) {
        this.db = db;
    }
}

Prefer constructor injection over field injection — it makes dependencies explicit and the class easier to test.

The same page, three toggle states, where each reader can set their own peferences and see only what they chose to.


Set the default tab in tab groups.

Let readers switch to their preferred view for tabgroups once, and this selection persists across your site.

  • Hover and click the     icon on any tab, or simply double-click it, to set it as your default across the site.
  • Prefer a cleaner view? Hide the tab bar entirely in the settings modal through the    icon on the left. Click the option to
    "show only the selected tab" to hide the navigation headers and show only your preferred content!
https://course-website.org/textbook/sorting
https://course-website.org/textbook/searching

Insertion Sort

Insertion Sort builds a sorted array one element at a time by picking each element and placing it in its correct position. Simple and efficient for small or nearly sorted arrays.

function insertionSort(arr) {
    for (let i = 1; i < arr.length; i++) {
        let key = arr[i];
        let j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
void insertionSort(int[] arr) {
    for (int i = 1; i < arr.length; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key

Time complexity: O(n²) worst case, O(n) best case on nearly sorted arrays.

Binary Search finds a target value in a sorted array by repeatedly halving the search space. Far more efficient than linear search for large datasets.

function binarySearch(arr, target) {
    let left = 0, right = arr.length - 1;
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (arr[mid] === target) return mid;
        else if (arr[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}
int binarySearch(int[] arr, int target) {
    int left = 0, right = arr.length - 1;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (arr[mid] == target) return mid;
        else if (arr[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}
def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

Time complexity: O(log n). Requires the array to be sorted before searching.

Switch to Python on either page, and every code example across the site follows.


Tweak placeholder text to match the reader's profile.

Let readers personalise your page to their needs.

  • Define a placeholder once, readers fill it in, and every instance across the site updates instantly. No backend required.
https://course-website.org/admin/setup

Course Instructions and Guide

Follow the steps below to set up your individual project repository. Enter your GitHub username once — all links and commands on this page will update to match.

Enter your GitHub username here:     GitHub Avatar

Step 1 — Fork and accept the invitation

Accept the GitHub Classroom invitation using your account at github.com[[username ? /$ : ]]. This will create a pre-configured fork of the exercise repository under your account.

Step 2 — Clone and get started

Run the command below to clone your repository to your local machine, then make your first commit to confirm everything is working.

git clone https://[[ username ? github.com/cs2103t/$-ip.git : your-repo-link ]]

Stuck? Raise an issue on the forum and include your GitHub username so tutors can locate your repository quickly.

Type a username above — every link, command, and path updates live.


Share selected parts of a page with others.

Link anyone directly to the exact paragraph, step, or answer they need. No more "scroll down and look for it."

https://course-website.org/textbook/week9
https://course-website.org/admin/faq

Liskov Substitution Principle

The Liskov Substitution Principle (LSP) states that objects of a subclass should be substitutable for objects of the superclass without altering the correctness of the program.

Common violation: Overriding a method in a subclass to throw an exception or do nothing breaks substitutability — callers written against the superclass will break unexpectedly.

Share  ·  Share with note

The principle encourages designing inheritance hierarchies where subclasses genuinely extend, rather than contradict, the behaviour of their parent.

Frequently Asked Questions

When is the submission deadline?

All submissions are due Friday 11:59 PM. Push to your main branch on GitHub.

Where do I find my team repo?

Your team repo is at github.com/course-[team-id]/project. If you cannot access it, check that you have accepted the GitHub Classroom invitation.

Share  ·  Share with note

How do I add my tutor as a collaborator?

Go to your repo → Settings → Collaborators → Add your tutor's GitHub username.

Click "Share" for a plain highlight, or "Share with note" to include a message and color. The URL encodes the element, color, position, and note — the recipient lands directly on that section.

Generating share links is built in. Add #cv-share to any page URL to enter share mode. Hover over any element on the page, add a note, and CustardUI generates the shareable URL for you. Links use content fingerprinting to stay robust even if the page text changes slightly.


Share your customisations with others.

Send a custom link that opens the page exactly as you intend, with placeholders pre-filled, sections pre-set. The recipient sees a personalised view instantly, no configuration needed.

https://course.org/setup?ph=username:johndoe,name:John%20Doe&t-hide=optional-steps

Course Setup Guide

This page was shared with your details pre-filled.

Welcome, John Doe! Here are your setup instructions.


Step 1 — Accept the classroom invitation

Accept the invitation at github.com/johndoe. This will create your personal fork of the exercise repository.

Step 2 — Clone your repository

git clone https://github.com/cs2103t/johndoe-ip.git
cd johndoe-ip

Step 3 — Push your first commit

git add .
git commit -m "Initial commit"
git push origin main

Optional troubleshooting steps are hidden for this view.

The URL that produced this view:

?ph=username:johndoe,name:John%20Doe&t-hide=optional-steps

ph= pre-fills placeholders. t-hide= hides toggle sections. A teacher can generate this link in seconds and send it directly to a student.


Advanced: let adopters create adaptations of your site.

Authors can expose selected content, such as logos, images, labels, and branding text, as adaptation points. Each organisation that adopts the site supplies a config preset overriding those values. The result is a single deployment that feels native to every audience it serves.

onboarding.example.com#/alpha-corp
onboarding.example.com#/beta-labs
  Alpha Corp — Onboarding Guide

Welcome to Alpha Corp! Follow the steps below to get set up.

Exercise 1 OPTIONAL

Complete the introductory module at your own pace.

Exercise 2 OPTIONAL

Read through the team handbook before your first standup.

Powered by the alpha-corp adaptation preset.

  Beta Labs — Onboarding Guide

Welcome to Beta Labs! Follow the steps below to get set up.

Exercise 1 COMPULSORY

Complete the introductory module before your first day.

Exercise 2 COMPULSORY

Read through the team handbook before your first standup.

Powered by the beta-labs adaptation preset.

Same page, same deployment — different logo, different branding, different labels. Each organisation supplies a config preset; the site adapts automatically.



How it works

All reader preferences, tab selections, toggle states, and placeholder values are saved in the browser's localStorage. Nothing is sent to a server, nothing leaves the device. You define exactly what can be customised; readers adjust within those boundaries.

CustardUI works across all modern browsers with no configuration needed on the reader's end.



Works with any static site

CustardUI is open source under the MIT licence. Add it to any static site with a single script tag without needing any build step, npm install, or backend.

Built and tested with MarkBind. Also works with Jekyll, Docusaurus, React static sites, and any site that serves plain HTML.

View on GitHub    •    npm package



How to Get Started

Up and running in minutes. Add one script tag. Write one config file.

1

Add the script tag

Drop one line of the CustardUI CDN script into your base layout or <head> template.

2

Create your config

Add a custardui.config.json to your site root. Define what readers can customise.

Done. Check the author guide for components and examples.



Custard Click
Custard Half
Custard Inspect
Custard Placeholder
Custard Share