cs349 notes

August 17, 2025 (2mo ago)

Intro

User Interface vs. User Interaction

Graphical User Interface

JavaScript

A closure occurs when an inner function retains access to the variables of its outer function, even after the outer function has finished executing. This allows the inner function to "remember" the environment in which it was created.

Example:

function outer() {
  let count = 0;
  return function inner() { //closure function
    return ++count;
  };
}
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2
 

In JavaScript, functions are treated as first-class citizens. This means functions can be assigned to variables, passed as arguments to other functions, and returned from functions.

Example:

const greet = () => "Hello";
const runFunction = (fn) => fn();
console.log(runFunction(greet)); // "Hello"
 

Equality Operators

== (loose equality): Compares two values after converting them to a common type.

Example: 5 == "5" → true

=== (strict equality): Compares two values without type conversion. They must be the same type and value.

Example: 5 === "5" → false

Prototype

Every JavaScript object has an internal [[Prototype]] object. This is the basis of prototype-based inheritance. One object can inherit properties and methods from another via the prototype chain.

Example:

const person = {
  greet: function() {
    return "Hello";
  }
};
const student = Object.create(person);
console.log(student.greet()); // "Hello"
 

Factory Function:A function that returns a new object each time it is called. Useful for creating multiple similar objects.

Example:

function createUser(name) { //this is a factory function
  return {
    name: name,
    greet: () => `Hi, I'm ${name}`
  };
}
const user1 = createUser("Alice");
const user2 = createUser("Bob");
 

Logical Operators

"", 0, null, undefined, false, NaN are falsy.

|| returns the first truthy value. Example: 0 || 55

?? returns the first value that is not null or undefined. Example: 0 ?? 50

?. optional chaining — allows safe access to deeply nested properties. Example: user?.profile?.email

undefined means variable is not defined

null intentionally set it as no value assigned

The class keyword simplifies working with constructor functions and prototypes. It provides cleaner, more familiar syntax for object-oriented development.

Example:

class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    return `Hi, I'm ${this.name}`;
  }
}
 

Arrays

Arrays are iterable objects in JavaScript. You can use loops or higher-order functions like map, filter, and reduce to work with them.

Example:

let arr1 = [];

Destructuring allows you to extract values from objects or arrays into variables. You can also rename them.

Example:

let obj = { a: 1, b: 2, c: 3 };
let { a: x, b: y } = obj;
console.log(x); // 1
console.log(y); // 2
 

Explanation: b: y means take the value of b and assign it to a variable called y.

The map() function creates a new array by applying a function to each element of the original array.

Example:

const arr3 = [1, 2, 3];
const arr4 = arr3.map(x => x * 10);
console.log(arr4); // [10, 20, 30]
 

TypeScript

What is TypeScript

TypeScript is a superset of JavaScript that adds optional static typing. It catches errors during compile time and improves developer tooling like IntelliSense, code navigation, and auto-completion.

Type Checking: TypeScript checks variable types at compile time.

Example:

let a = 123;
a = a + "hello"; // Error: string is not assignable to number
 

Type Annotations: You can explicitly specify the type of variables using a colon.

let n: number;
let s: string;
let b: boolean;
 

Implicit Types

TypeScript can detect the type if a value is assigned immediately.

let n = 123; // inferred as number

If a type cannot be inferred, it becomes any, which disables type checking.

Array Types

There are two main ways to declare arrays with specific types:

let arr1: number[];
let arr2: Array<number>;
 

You can specify the type for function parameters and the return value.

Example:

function add(a: number, b: number): number {
  return a + b;
}
 

You can define reusable type definitions.

Example:

type NumberArray = number[];
 

Interfaces describe the structure of an object.

Example:

interface Point {
  name: string;
}
 

Union types allow a variable to hold multiple types.

Example:

let id: number | string;
 
// most commonly used in function arguements
 
function printID(id: number | string) {
	console.log(`Your ID is ${id}`);
}
 

This means id can either be a number or a strings

TypeScript automatically narrows union types using conditions:

function formatId(id: number | string): string {
  if (typeof id === "number") {
    return `${id}`;
  } else {
    return id.toUpperCase();
  }
}

TypeScript introduces an enum type:

enum State {
	Idle,
	Down,
	Up,
}
let state1: State = State.Idle

Summary

🧩 Primitive Types


let age: number = 20;
let name: string = "Faiz";
let isActive: boolean = true;
let anything: any = "can be anything"; // ⚠️ avoid if possible
let maybe: unknown = 42;
 

🧺 Arrays

let names: string[] = ["Ali", "Faiz"];
let scores: Array<number> = [90, 85];
 

📦 Objects

let user: { name: string; age: number } = {
  name: "Faiz",
  age: 21
};
 

🏷️ Type Aliases

type Point = { x: number; y: number };
let p: Point = { x: 5, y: 10 };
 

🔀 Union Types

type ID = number | string;
let userId: ID = 123;
userId = "abc";
 

🎯 Literal Types

type Status = "success" | "error" | "loading";
let s: Status = "success";
 

🛠️ Functions

function add(a: number, b: number): number {
  return a + b;
}
 
function greet(name: string = "Guest") {
  console.log("Hello, " + name);
}
 

🧱 Interfaces

interface Car {
  make: string;
  model: string;
  year: number;
}
 
const car: Car = {
  make: "Toyota",
  model: "Camry",
  year: 2022
};
 

📉 Type Narrowing

function printId(id: number | string) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id);
  }
}
 

🔗 Tuples

let pair: [string, number] = ["score", 100];
 

🚫 Common Mistakes to Avoid


💡 Tips


Drawing

mvc

mvc

The windowing system is an operating system layer to share screen space and user input among applications

it provides three man services

  1. manage list of windows: creating, resizing, focusing, etc
  2. provide each application with an independent drawing area
  3. dispatch low level input events to the focused window

a modern web browser is like a windowing system where it manages a list of tabs, provides each tab with an independent drawing area, etc

image.png

Drawing Primitives

image.png

image.png

HTML canvas (HTMLCanvasElement) is a literal “canvas abstraction”

import startsimpleKit, setSKDrawCallback and start it up via startSimpleKit()and then set the drawing callback

setSKDrawCallback((gc) => {
  gc.fillRect(10, 10, 50, 50);
});
// is a function that tells the system how to draw
//everything on the screen EACH FRAME
 

image.png

save() to push the current drawing state to stack

store() to pop last saved drawing state and restore it

A display list is a list of what to draw (not actual pixels). It stores objects (like rectangles or circles), and you redraw them each frame. It avoids re-rendering everything manually. (basically: a list of drawable objects in order to be rendered)

//first circle before saving state
gc.fillStyle = "blue";
gc.strokeStyle = "red";
gc.lineWidth = 5;
circle(50,50); 
 
gc.save();
 
//second circle before saving state
gc.fillStyle = "yellow";
gc.strokeStyle = "black"
gc.lineWidth = 2;
circle(110,50);
 
gc.restore(); //restorign state to the first cicle
 
circle(170,50);

image.png

Instead of writing all drawing code manually every time, we create reusable objects that know how to draw themselves.

  1. Define a Drawable Interface
export interface Drawable {
  draw: (gc: CanvasRenderingContext2D) => void;
}
 

This means any object that implements this interface must have a .draw() method.

  1. Make a Drawable Class
export class MyShape implements Drawable {
  draw(gc: CanvasRenderingContext2D) {
    // put drawing commands here (like fillRect, stroke, etc.)
  }
}
 

You can now define any shape or UI element inside a class.

  1. Use the Object to Draw
const myShape = new MyShape(...);
myShape.draw(gc);
 

The shape takes care of its own drawing using the graphics context (gc).

Painter’s Algorithm

Input Events

Event-Driven programming is a programming paradigm that bases program execution flow on events. These can represent user actions or other system actions.

Fundamental Events: raw events directly from the browser

Translated Events: higher-level processed events derived from fundamentals

Types of Event Devices:

Purpose of Event Queue in SimpleKit run loop: stores input events so they can be processed in order during each animation frame, ensuring smooth UI updates

The transformed low level input is a state

the windowing system generates events when the state changes:

key differences between keydown and mousemove is that

setSKDrawCallback() 
//sets the function that gets 
//called on every drame to draw your objects
//it does not draw directly, it tells SimpleKit: 
//"here's how to draw everything when it's time"

| Aspect | keydown | mousemove | | --- | --- | --- | | Triggered by | Keyboard key press | Mouse movement | | Frequency | Once (or repeated if held) | Continuously while mouse moves | | Used for | Commands, controls, toggles | Tracking cursor, live drawing | | Data accessed | e.key | e.x, e.y | | Common combo | keydown + keyup for control | mousemove + mousedown for drag |

each event is associated with a timestamp

event queue is a buffer between the user and each window. user input events tend to be bursty

createWindowSystem

const eventQueue: FundamentalEvent [] = [];

image.png

image.png

image.png

Hit Testing

Hit-Testing is the process of checking whether a user’s mouse or touch input interacts with a visual element (shape) on the screen. It’s used for detecting clicks, dragging, hovering, etc.

Shape Model vs. Shape Image

image.png

Common Shape Representations

Hit Test Paradigms

Examples:

// Circle Inside Hit-Test
(dx * dx + dy * dy) <= r * r
 
// Rectangle Inside Hit-Test
mx >= x && mx <= x + w &&
my >= y && my <= y + h
 
// Circle Edge Hit-Test
const dist = sqrt(dx*dx + dy*dy);
r - s/2 <= dist && dist <= r + s/2
 

Hit-Testing Considerations

Optimizing Hit-Testing

Canvas API Support

Hit-Testing in Practice

Animation

Animation: is the simulation of motion through a sequence of images or drawings that change over time.

Frames and Frame Rate

| Term | What it means | | --- | --- | | Frame | One single image or moment in an animation | | Frame Rate | How many frames are shown per second (e.g., 60 fps = 60 Hz) | | Tweening | Interpolating (filling in) frames between two "key frames" | | Key Frame | A frame that marks a major change (e.g. start or end of motion) | | Easing | A function that controls how the speed/feel of tweening changes |

Simulation

Animating by simulation means using rules and functions to update the state every frame (no start/end, it just loops). For example:

dot.x += dx;
dot.y += dy;

If it hits the edge, reverse direction:

if (dot.x < dot.r || dot.x > box.width - dot.r) dx *= -1;
 

Timers

Timers control when an animation or event should happen.

Tweening (Interpolation)

Tweening creates smooth transitions between two values.

// linear interpolation from start to end
// using normalized time t (in [0,1])
const lerp = (start, end, t) => start + (end - start) * t;
 

Where t is (currentTime - startTime) / duration.

Easing Functions

Easing changes the feel of an animation (makes it less linear).

image.png

value = lerp(startValue, endValue, easing(t));


Keyframes

[
  { time: 0, value: 100 },
  { time: 1000, value: 300 },
  ...
]


Animation in SimpleKit

Use setSKAnimationCallback((time) => { ... }) to animate each frame.

Use it to update positions, change colors, and control timing.


Animator Class in SimpleKit

export class Animator {
  constructor(
    public startValue: number,
    public endValue: number,
    public duration: number,
    public updateValue: (v: number) => void,
    public easing: EasingFunction = (t) => t
  ) {}
}

Call update(time) every frame to animate and interpolate value.

Animation Manager Pattern

An animation manager holds multiple animations and updates them all each frame:

manager.update(currentTime);

Removes finished ones automatically.

System Timers and UI Threading

Example Use Case

Widgets

image.png

Widget Functions

image.png

Widget Types

| Abstract Widget | What it Does | Examples | | --- | --- | --- | | Action | Triggers an action | Button, Menu | | Number | Input or show a number | Slider, Spinner | | Boolean | Toggle true/false | Checkbox, Toggle | | Choice | Pick from a list | Dropdown, Radio Group | | Text | Show/edit text | Label, TextField | | Container | Holds other widgets | Panel, Tabs |

//widget example: button
class SKButton extends SKElement {
  draw(gc) { //draw() shows how it looks
    if (this.isHovered) { //if the mouse hovers over, turn it into light blue
      gc.fillStyle = "lightblue";
    } else {
      gc.fillStyle = "white";
    }
    gc.fillRect(this.x, this.y, this.width, this.height);
    gc.fillText(this.label, this.x + 10, this.y + 20);
  }
 
  onClick() { // triggers when clicked and emits an action
    this.sendEvent("action");
  }
}
//Widget Example: Label
//Just displays text — no interaction.
 
class SKLabel extends SKElement {
  draw(gc) {
    gc.fillText(this.text, this.x, this.y);
  }
}
 
//Widget Example: Checkbox (Boolean Abstract Widget)
 
class SKCheckbox extends SKElement {
  constructor(props) {
    super(props);
    this.checked = false;
  }
 
  draw(gc) {
  
    gc.strokeRect(this.x, this.y, 20, 20);
    if (this.checked) {
      gc.beginPath();
      gc.moveTo(this.x + 4, this.y + 10);
      gc.lineTo(this.x + 10, this.y + 16);
      gc.lineTo(this.x + 16, this.y + 4);
      gc.stroke();
    }
  }
 
  onClick() {
    this.checked = !this.checked;
    this.sendEvent("value changed");
  }
}
 
//container widgets where it hold widgets and lay them out.
// think of it like a parent <div> in HTML
 
class SKPanel extends SKContainer {
  constructor(props) {
    super(props);
    this.children = [];
  }
 
  addChild(widget) {
    this.children.push(widget);
  }
 
  draw(gc) {
    for (const child of this.children) {
      child.draw(gc);
    }
  }
}
 
//widgets can use a shared Style class, can treak fonts, colors, etc
const Style = {
  widgetHeight: 30,
  font: "14px sans-serif",
  color: "blue",
};
 

Dispatch

Dispatch refers to the routing of an event to the appropriate widget or code that needs to handle it. Dispatch takes care of who was clicked (which widget) and how should it handle (what function to call) by finding the right widget based on mouse position or focus and sending the event to that widget so it can react

In UI Architecture: to route an event to the appropriate widget/code

UI Event Flow (Pipeline)

The event moves through the following stages:

  1. Low-Level Input (OS/device)
  2. Event Creation (Toolkit layer)
  3. Event Queue (Queued in browser)
  4. Dispatch (Toolkit routes it to widgets)
  5. App Binding (Your code handles the event)

View Hierarchy

image.png

const blueContainer = new SKContainer({ fill: "lightblue" });
const greenContainer = new SKContainer({ fill: "lightgreen" });
const buttonA = new SKButton();
const buttonB = new SKButton();
 
blueContainer.addChild(buttonB);
blueContainer.addChild(greenContainer);
greenContainer.addChild(buttonA);
 

image.png

Dispatch Steps (uses DFS)

  1. Target Selection

    Find which widget is under the mouse or has focus (positional vs focus).

  2. Route Construction

    Build a path from the root to the target widget.

    builds a list of widgets from the root of the widget tree to the target widget. It includes all ancestors of the target and is used during event propagation (capture and bubble phases).

  3. Propagation

    Two phases:

    • Capture (DOWN): From root to target
    • Bubble (UP): From target back to root
function dispatch(me: SKMouseEvent, root: SKElement) {
  const route = buildTargetRoute(me.x, me.y, root);

  const stopPropagation = route.some((el) => el.handleMouseEventCapture(me));
  if (stopPropagation) return;

  route.reverse().some((el) => el.handleMouseEvent(me));
}

2 Main Type of Dispatch:

Event Binding Mechanisms

Global Event Callback (Old Windows)

A single function like WindowProc handles all events.

A widget uses stopPropagation() when it wants to handle an event without letting other widgets (like its parents) also react. This is useful when, for example, a button inside a list item is clicked — you want the button to perform its action, but stop the event from reaching the list, so the list doesn't also think the item was selected.

switch (uMsg) {
  case WM_KEYDOWN:
    // handle key
    break;
}
 

Inheritance Binding

Widgets extend a base class with generic handlers like:

onMouse(e), onKeyboard(e), onMouseClick(e)

Listener Binding (JavaFX Style)

textField.setOnKeyPressed(event -> {
  if (event.getCode() == KeyCode.ENTER) { ... }
});
 

SimpleKit Binding (Used in Course)

button.addEventListener("click", (e) => {
  console.log("Button clicked!");
});

this.bindingTable.push({ type: "click", handler });

2 Phases of Event Propagaton: Capture vs Bubble

Mouse Enter / Exit Events

Widgets can receive hover-like events:

Used for hover effects.

Focus Management

Example: Add Two Numbers App

Widgets:

plusButton.addEventListener("click", (e) => {
  const val1 = parseInt(input1.getText()) || 0;
  const val2 = parseInt(input2.getText()) || 0;
  resultLabel.setText("= " + (val1 + val2));
});
 

Layout

Box Model:

Widget Sizes:

SimpleKit Box Model Implementation

2 Types of Layout (Two Pass Layout)

  1. Measure – All widgets calculate their intrinsic size.
  2. Layout – All widgets are positioned and sized based on layout strategy.
function layoutRoot() {
  uiTreeRoot.measure(); // Step 1: Measure intrinsic sizes
  uiTreeRoot.layout(gc.canvas.width, gc.canvas.height); // Step 2: Apply positions/sizes
}
 

fillWidth and fillHeight are properties that tell the layout system to stretch the widget to fill the available space horizontally and vertically inside the container.

Layout Implementations

FixedLayout

Widgets manually set their x, y, width, height.

root.layoutMethod = new Layout.FixedLayout();

CentredLayout

All widgets stack on top of each other, centered in the parent container.

root.layoutMethod = new Layout.CentredLayout();

WrapRowLayout

Widgets are placed in rows, wrapping when space runs out.

root.layoutMethod = new Layout.WrapRowLayout({ gap: 10 });

FillRowLayout

Widgets are resized proportionally to fill the container row.

root.layoutMethod = new Layout.FillRowLayout({ gap: 10 });
widget.fillWidth = 1; // Proportional size

Layout Invalidation

When any size/layout property changes, layout needs to be redone.

To prevent multiple recalculations per frame, SimpleKit uses a flag:

let layoutRequested = false;
function invalidateLayout() {
  layoutRequested = true;
}

Only one layout update runs per animation frame.

Model View Controller (MVC)

image.png

  1. Separate data, state, and "business logic" from user-interface
    • Ideally, View and Controller implementations can change without changing Model implementation, e.g.:
    • Add support for a new interface (e.g. different device)
    • Add support for a new input device (e.g., touchscreen)
  2. Supports multiple views of same data, e.g.
    • View numeric data as a table, a line graph, a pie chart, …
    • Present simultaneous “overview” and “detail” views
    • Distinct “edit” and “preview” views
  3. Separation of concerns in code
    • code reuse
    • unit testing

MVC Implementation

Interface architecture decomposed into three parts:

image.png

  1. Model stores the data and has logic to update it
  2. When the Model changes, it notifies Views (via Observer pattern)
  3. View then refreshes its display
  4. Controller translates user input into model updates

Basic Code Architecture

Model

export class Model extends Subject {
  private _count = 0;
 
  get count() { return this._count; }
 
  increment() {
    this._count++;
    this.notifyObservers();
  }
}
 

View

export class LeftView extends SKContainer implements Observer {
  update(): void {
    this.button.text = `${this.model.count}`;
  }
 
  button: SKButton = new SKButton({ text: "?" });
 
  constructor(private model: Model, controller: LeftController) {
    super();
    this.addChild(this.button);
 
    this.button.addEventListener("action", () => {
      controller.handleButtonPress();
    });
 
    this.model.addObserver(this);
  }
}
 

Controller

export class LeftController {
  constructor(private model: Model) {}
 
  handleButtonPress() {
    this.model.increment();
  }
}
 

MVC Variants

  1. MVA (Model-View-Adaptor)
    • Adaptor helps View interpret the Model.
  2. MVP (Model-View-Presenter)
    • Presenter formats data from Model and handles inputs.
    • View & Presenter are tightly connected.
  3. MVVM (Model-View-ViewModel)
    • Uses data-binding so View ↔ Model updates are automatic.

HTML CSS

 
<div>: block-level element used as a container.
 
<span>: inline container, used for styling small pieces of text.
 
<button>, <input>, <form>: interactive form elements.
 
<script>: loads JavaScript into the HTML page.
 

DOM (Document Object Model)

CSS (Cascading Style Sheets)

CSS Rules

image.png

CSS Selectors

| Selector | Description | Example | | --- | --- | --- | | Tag | Targets all elements of that type | div {} | | Class | Targets elements with a class | .box {} | | ID | Targets an element with a specific ID | #header {} | | Attribute | select elements matching attribute value | [type="text"] {} | | Pseudo | Targets based on state | button:hover {} |

Style Example

.red {
  background-color: red;
  color: white;
}
#main-title {
  font-size: 2rem;
}
 

Flexbox (Flexible Box Layout)

Styling with Classes vs IDs

Inline vs Internal vs External CSS

Declarative vs Imperative

Visual Design

Visual design focuses on how your UI looks and feels. It helps users navigate interfaces more easily and enjoy the experience.

Design Goals

Core Principles of Visual Design

Typography Tips

Color Use

Icons and Imagery

Grid Systems

Accessibility Considerations

Typeface vs Font

A Design System is a systematic approach to product development, complete with guidelines, principles, philosophies, and code

Primary Colors: main brand/interface, usually detailed in the layout, key elements, text, etc

Secondary Colors: used for functional meaning such as alert

Text

Text Representation and Encoding

Text is not stored as characters but as numbers. These numbers are known as code points.

Internationalization (i18n) vs Localization (l10n)

HTML Forms

Input Attributes:

Client-Side Validation

Handled in the browser before the form is submitted. Helps prevent incorrect or missing data from reaching the server.

Types of Validation:

  1. Built-in HTML Validation
    • Example:

      <input type="email" required>
  2. JavaScript Validation
    • Example:

      if (!email.includes("@")) {
        alert("Please enter a valid email");
      }
  3. Constraint Validation API
    • Provides programmatic access to validation rules.

    • Example:

      if (!input.validity.valid) {
        showError();
      }
       

Input Formatting vs Input Masking

Why It Matters: Both help improve readability and user experience while reducing errors.

Input Sanitization

Users can input harmful data. If not sanitized, this can lead to:

Sanitize both client-side and server-side:

Undo

4 Design Choices:

Implementation Approaches:

Implementation with Stacks

Using either of these options requires two stacks

Memento

Direct Manipulation

| Real World | Interface | | --- | --- | | Move hand to object | Move pointer to object | | Pick up with hand | Click and hold | | Carry to trash bin | Drag to trash icon | | Release | Release mouse button |

Benefits:

Dragging

image.png

Transformable Shape:

Instrumental-Interaction

3 Properties to describing instruments

image.png

image.png

| Feature | Direct Manipulation | Instrumental Interaction | | --- | --- | --- | | Interaction Style | Directly interact with the object itself | Use a separate tool (instrument) to act on the object | | Pros | - Feels intuitive and natural- Immediate feedback- Easy to learn | - More flexible and powerful- Reusable instruments- Supports complex actions | | Cons | - Limited to simple tasks- Can clutter the UI- Not reusable | - Higher learning curve- More indirect- Visual/tool complexity | | Example | Dragging a shape to move it | Using a slider to resize an object | | Best For | Simple, direct interactions | Advanced, multi-step, or customizable interactions |

Summary:

✅ Pros:

⚠️ Cons:

Asynchronous

System performance measures how fast the system actually runs tasks, while

perceived responsiveness is how fast the user feels the system is responding — even if it’s still processing in the background.

Event Loop: The event loop checks continuously if there are tasks waiting in the task queue or microtask queue and pushes them to the call stack when it's empty. This allows the UI to stay responsive by deferring slow or blocking operations to be handled later.

A responsive UI feels like it responds in a timely manner

it is usually accomplished in two ways:

continuous latency

input feedback

image.png

graceful degradation of feedback

busy indicators

progress bars:

progressive loading

image.png

asynchronous execution:

Prediction Strategy: use idle time to pre-compute or pre-load the next actions that is likely so they happen instantly when requested.

Web Worker:

| Feature | Normal Async Execution (Promises, fetch) | Web Worker | | --- | --- | --- | | Threading | Single-threaded (still runs on the main thread, just delayed until call stack is clear) | Runs in a separate thread from the main UI | | Purpose | Non-blocking I/O tasks (network requests, timers) | Offload heavy computation so the UI remains responsive | | Example | fetch(), setTimeout() | Image filtering, parsing large datasets | | UI Access | Can directly update DOM | Cannot touch DOM (must send messages back) | | Execution Model | Uses event loop + task/microtask queues | Runs independently and communicates via message passing |

Declarative

Imperative Programming

Declarative Programming

image.png

HyperScript

div { class "container"} 
p "some text"
button "Ok"
 
//this is equivalent to below
 
<div class="container">
	<p> some text </p>
	<button>Ok</button>
</div>

Preact

function MyComponent(props: { msg: string }) { 
//msg:string is a custom prop
return (
	<div class="container">
	  <p>{props.msg}</p>
	  <button>Ok</button>
  </div>
 );
}
const vdom = (
	<div>
		<h1>my component is below here: </h1>
		<MyComponent msg='hi'/>
	</div>
);

Component Children:

function MyCard(props) {
  return <div class="card">{props.children}</div>;
}
 
<MyCard> //this is a component
  <h2>Title</h2>
  <p>This is some content.</p>
</MyCard>
 ****

Hook: A special function in Preact/React that adds extra features (like state or side effects) to functional components, and must be called in the same order every render.

Reactivity

image.png

image.png

image.png

Approaches to Managing Application State

  1. useState hook for local component state
    1. pass state to children
    2. manage state with useState hooks
    3. state is stored in root component
    4. passed to children using props
function User() {
	const [name,setName] = useState("Bob");
	const [age,setAge] = useState("13");
	const [online,setOnline] = useState(true);
  1. useContext hook to access state

    1. without passing as props
    2. Provider wraps components → children use useContext to get values.
    3. similar to shared backpack that any component can open and grab data from - no matter where they are in the tree
    4. you put the data in the backpack using a Provider at a high level
    5. this is quite similar to themeProvider
  2. Signals

    • From @preact/signals.
    • Export signals + mutations from a state module.
    • Minimal, well-organized, used only where needed.
    • Reassign arrays/objects to trigger updates.

    Best Practices for Signals

    • Keep state small and focused.
    • Avoid too many signals (performance).
    • Use useState for local state; signals for shared/global.
    • Keep components small and focused.
import { signal } from "@preact/signals";

// state
export const count = signal(0);

// mutations
export const increment = () => {
  count.value++;
};

import {signal} from '@preact/signals';
export const count = signal(0);
export const increment = () => {
	count.value++
};

Effects

Component Data Flow

image.png

Styling

Tailwind is a utility-first CSS framework that styles elements with short class names, supports responsive and state-based styling, and removes unused CSS in production for efficiency—ideal for declarative UI components.

Ways To Style Components

Inline CSS

"padding: 10px" -> { padding:"10px" }
"padding: 10px" -> { padding:"10" } 
//by default, it's translated to px
//JavaScript var names cannot have '-' char so 
//it is in camelCase
"flex-flex: row nowrap" -> {flexFlow: "row nowrap"}

CSS Modules

css file with 'module.css' extension

generates unique classname so styles only affect that component

import styles from './MyComponent.module.css';
<div className={styles.root}>

Tailwind

Accessibility

Definition: developing content to be as accessible as possible, no matter an individual’s physical and cognitive abilities and how they access a user interface. often abbreviated as a11y

Access to User Interface is considered as human right

Abilities

image.png

Types of Long-term Impairments

OS Accessibility Features

Enhancements

Curb Cut Effect: Accessibility features often help everyone (e.g., captions help in noisy areas, learning languages).

Legal Requirements

Four WCAG Principles:

Implementing Accessibility

image.png

image.png

Testing