Ayaan Gill.

Designer & Engineer

I started in product design, got into engineering, and now spend most of my time somewhere between the two.

api/sessions.md

modified+1−1
CodePreview

Create a session

POST /v1/sessions

Request body

PropertyTypeDescription
workspace_idstringYour workspace ID.
expires_inintegerToken expiration.
expires_inintegerSession lifetime.
scopesarraySession permissions.

Résumé

Sandgarden

Layer

USC

Master’s

UCSD

Bachelor’s

Résumé

Ayaan Gill’s résumé, including experience, education, and technical skills. Download the PDF for selectable text.
Under construction
Up nextPhiladelphia
Marathon
—days to go

A nametag of my own

I went to a few events that didn’t have nametags, so I made my own. The QR code saves me digging through LinkedIn when it’s time for the obligatory networking ritual.

The QR code opens a little page with a neat animation. Scan it with your phone to see it, or open the page here. It’s only optimized for mobile, because who scans a QR code on their computer?

Why didn’t you put an NFC tag in it, you ask?

Because it’d be kinda weird to ask someone to tap their phone to my chest.

De-Slopping Your AI Prototypes

A few guardrails for designers building with coding agents.

You asked Claude Code to build a dashboard. It looks pretty good! Except it has six slightly different shades of gray, four border radii, and a button that's actually a div with an onClick.

You could add another paragraph to your AGENTS.md asking it to follow your design system. Maybe put IMPORTANT in all caps this time.

Or you could make it follow some actual rules.

Meet your new best friend: the linter

A linter is a tool that checks your code against a set of rules and tells you when something breaks them.

Unlike the instructions you give your coding agent, these rules are enforced mechanically. The agent doesn't get to decide whether it feels like following them today.

For example, you can tell it not to use arbitrary spacing values, require semantic HTML, or flag certain accessibility mistakes.

And the best part? You don't have to fix everything yourself. Your agent can run the linter, read the errors, and fix its own mess.

AGENTS.md

Please use our design tokens.
IMPORTANT.

Ignored by your agent

$ npm run lint

error Unapproved spacing value
p-[17px]

A rule your agent has to follow

A linter won't make your designs good. But it can stop your agent from making the same avoidable mistakes over and over.

Stop inventing design tokens

If you're using Tailwind, you've probably seen classes like these:

<div className="p-[17px] rounded-[13px] bg-[#F8F9FB]">

There's nothing inherently wrong with any of those values. But if your agent invents new ones for every component, your carefully considered design system quickly becomes a collection of suspiciously similar numbers.

Tailwind already provides a spacing scale and utilities that can reference your design tokens. You can also configure ESLint rules to flag arbitrary values or restrict specific classes.

For example, tailwindcss/no-unnecessary-arbitrary-value flags arbitrary values when an equivalent standard utility exists. For stricter enforcement, eslint-plugin-better-tailwindcss provides configurable restrictions on classes.

Project overview

Everything in one place.

Invented values

  • p-[17px]

    Adds a spacing value outside the shared scale.

  • rounded-[13px]

    Introduces another nearly identical corner radius.

  • bg-[#F8F9FB]

    Hard-codes a color instead of reusing a token.

Project overview

Everything in one place.

Shared tokens

  • p-4

    Reuses the spacing scale.

  • rounded-xl

    Keeps corners consistent across components.

  • bg-white

    Uses an existing color utility.

The goal isn't to ban every arbitrary value. Sometimes you really do need an unusual measurement.

It's to make the agent reuse decisions you've already made instead of reinventing your spacing system every time you ask for a modal.

Your clickable div is not a button

Here's a classic:

<div onClick={handleSubmit}>
  Submit
</div>

Looks like a button. Works when you click it. Ship it?

Not quite.

A real <button> comes with keyboard interaction, focus behavior, and semantics that browsers and assistive technologies already understand.

<button type="button" onClick={handleSubmit}>
  Submit
</button>

The eslint-plugin-jsx-a11y package catches common accessibility mistakes in React, including non-interactive elements being used as controls, missing image descriptions, and invalid accessibility attributes. (If you’re using React Native, try eslint-plugin-react-native-a11y.)

Use Tab to reach the button, then Enter or Space to activate it.

Submit

A plain div. Skipped by Tab.

0 activations

A native button. Keyboard ready.

0 activations

Of course, linting your JSX isn't enough to make a site accessible.

Tools like axe-core can check the rendered page for additional issues, including detectable color-contrast failures and missing accessible names. You can run axe through browser developer tools or automated tests.

Neither tool catches everything. You'll still want to test with a keyboard, and sometimes a screen reader.

But at least you can stop shipping clickable divs and calling it a day.

A few more rules worth stealing

Once you've got the basics, there are other checks that can save you from surprisingly annoying problems.

Conflicting Tailwind classes. Tools such as eslint-plugin-better-tailwindcss can flag duplicate or conflicting utilities before you spend twenty minutes wondering why a style isn't applying.

Unrestricted CSS values. If you're writing regular CSS instead of Tailwind, Stylelint can enforce approved property values. Your design tokens don't have to disappear just because you're not using utility classes.

You don't need fifty rules on day one. Start with the mistakes you actually keep seeing.

Make the agent clean up after itself

Installing a linter is only half the job. You also need to make sure it actually runs.

A simple workflow looks like this:

  1. Generate UI

    Claude Code~/portfolio

    Add a project overview card using our existing UI components.

    Enter to send

  2. Run lint

    $ npm run lint

    error p-[17px]: use the spacing scale

    error rounded-[13px]: use a radius token

    error bg-[#F8F9FB]: use a color token

    3 violations

  3. Agent fixes the code

    Claude Code

    Fixing 3 violations. Replacing the custom values with existing design tokens.

    Update(component.tsx)

    − p-[17px] rounded-[13px] bg-[#F8F9FB]

    + p-4 rounded-xl bg-white

  4. Check again

    $ npm run lint

    ✓ No violations. Ready to review.

Configure your rules, add a lint command to your project, and tell your agent to run it after making changes.

For example, your AGENTS.md might include:

AGENTS.md
After modifying frontend code:
- Run `npm run lint`.
- Fix any lint errors introduced by your changes.
- Don't disable rules to make errors disappear.

If you're working with a team, run the same checks in CI so violations can block a merge rather than quietly sneaking into production.

One important detail: ESLint doesn't magically prevent bad code from being written. It detects violations when it runs. Your agent still needs to respond to the errors, and your workflow needs to enforce the result.

The difference is that you're no longer relying entirely on the agent remembering your preferences.

Instead of asking nicely for the fourteenth time, you've given it a test it needs to pass.

Unfortunately, taste is still your problem

None of this will tell you that your dashboard has too many cards, your onboarding flow makes no sense, or that the entire interface looks like every other AI-generated SaaS product.

That's still your job.

But if the tooling can enforce the decisions you've already made, you get to spend less time fixing inconsistent padding and more time making the decisions that actually require a designer.

And maybe, just maybe, your next prototype will only have one shade of almost-white.