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
Hide irrelevant sections; Collapse less relevant sections.
Let readers collapse what they don't need. Their choice sticks across pages and sessions.
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.
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.
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.
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.
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: 
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."
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.
The principle encourages designing inheritance hierarchies where subclasses genuinely extend, rather than contradict, the behaviour of their parent.
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.
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.
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.
Same page, same deployment — different logo, different branding, different labels. Each organisation supplies a config preset; the site adapts automatically.
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.
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.
Up and running in minutes. Add one script tag. Write one config file.
Done. Check the author guide for components and examples.




