Duolingo’s design language — flat fills on a hard under-edge, rounded geometry, one loud primary — rebuilt around SGT University’s blue #0060aa, which is oklch(0.4837 0.1403 250.89).

Brand

#0060aa is unusual and lucky: at 6.45:1 it passes AA both as a fill under white text and as text on white, so it can be either without a second thought.

  • brand#0060aaPrimary fill and primary text. Passes AA in both directions.6.45:1AA body
  • brand-edge#00458dThe under-edge beneath every brand button.9.39:1AA body
  • brand-ink#135895Brand-coloured text on white. AAA.7.35:1AA body

Brand ramp

Stepped in OKLCH at the brand's own hue, so every rung is the same blue rather than drifting purple in the shadows. The lower rungs are the trap: brand-50 through brand-400 must never carry text.

  • brand-50#eff7ffSurface tint1.08:1Non-text only
  • brand-100#daecffSurface tint1.21:1Non-text only
  • brand-200#bbdbfeBorder1.43:1Non-text only
  • brand-300#92c2f6Border1.86:1Non-text only
  • brand-400#65a3e3Decorative2.66:1Non-text only
  • brand-500#3b84ccLarge text / UI edges3.92:1AA large / UI
  • brand-600#1c6bb3AA body text5.52:1AA body
  • brand-700#135895AAA body text7.35:1AA body
  • brand-800#124676AAA9.71:1AA body
  • brand-900#0f3458AAA12.71:1AA body
  • brand-950#09223aDark-mode canvas16.13:1AA body

Semantic colours

Each fill is solved so white text on it clears 4.5:1. Amber is the exception and the classic trap — no amber light enough to read as amber can hold white text, so it takes dark ink at 11.42:1.

Success

#198639

4.66:1 · white on fill

Warning

#ffc800

11.42:1 · DARK ink on fill

Danger

#d92d20

4.83:1 · white on fill

Brand

#0060aa

6.45:1 · white on fill

Neutrals

  • ink#101828Primary text17.75:1AA body
  • ink-2#344054Headings on tint10.46:1AA body
  • muted#475467Secondary text7.69:1AA body
  • muted-2#667085The floor — lightest grey that still passes AA for body4.97:1AA body
  • line#d0d5ddBorders only1.47:1Non-text only
  • line-soft#eaecf0Dividers only1.18:1Non-text only
  • surface#f7f9fcPage tint1.05:1Non-text only

The 3D button

A flat fill on a 4px hard under-edge that collapses on press, so the control physically sinks under the finger. Built with box-shadow rather than border-bottom, so pressing never reflows the layout around it. Press one.

Typography

Nunito — our choice, not Duolingo's: there is no public Duolingo font guidance to cite. It is rounded, warm, freely licensed and has good numerals. One deliberate departure from the reference implementation: display type here does NOT lowercase. This portal sets student names, company names and registration numbers, and lowercasing a registration number is a data bug wearing a typographic costume.

display · 28px

No Objection Certificate

display · 21px

CS-402 · Database Management Systems

body · 14px

The internship covers 60% of the syllabus, weighted by contact hours.

tnum · tabular figures for all data

Registration22010112345
ReferenceSGTU/NOC/2026/00418
Mapped60%
Issued30 Aug 2026

Tabular figures so a column of numbers doesn’t jitter between rows.

Motion

Enter uses ease-out; exit runs at about 65% of enter. Transform and opacity only. Hover a tile on a mouse, or press play — a curve you can only feel with a cursor is a curve half the people reading this cannot feel at all.

--ease-out-quart

cubic-bezier(0.25, 1, 0.5, 1)

The default. Fast out of the gate, gentle landing.

--ease-out-back

cubic-bezier(0.34, 1.56, 0.64, 1)

Overshoots slightly. For arrivals worth noticing.

--ease-in-out-soft

cubic-bezier(0.65, 0, 0.35, 1)

Symmetric. For things that move between two places.

  • Button feedback 100–160ms
  • Hover 120–200ms
  • Modals 200–300ms
  • Exit ≈65% of enter
  • Entrances ease-out, never ease-in
  • Reduced motion keeps short fades, drops movement

Status

Status is never communicated by colour alone — every state carries a word, because roughly 1 in 12 men has a colour vision deficiency and a red dot beside a green dot is not a message.

ApprovedAwaiting DeanChanges requestedRejectedCompany unreachableDraft

The mobile floor

Each of these is one line of CSS in globals.css, and each is visible the moment it is missing. Students will open this on a phone on campus Wi-Fi.

  • Hover sticks after tap@media (hover: hover) and (pointer: fine)
  • Grey flash on tap-webkit-tap-highlight-color: transparent
  • Wrong height with browser chrome100dvh / 100svh
  • Page zooms on focused inputfont-size: max(16px, 1em)
  • Tap feels laggytouch-action: manipulation
  • Pull-to-refresh eats the formoverscroll-behavior: none
  • Content under the notchviewport-fit=cover + env(safe-area-inset-*)
  • Long-press selects button textuser-select: none on controls only
  • Status bar colour mismatchtheme-color per colour scheme