Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Prompt Engineering

This file was deleted.

91 changes: 91 additions & 0 deletions Round 1 - Vague prompt/index.html/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Settings</title>
<style>
:root { color-scheme: light; --ink: #172033; --muted: #6b7280; --line: #e7e9ee; --surface: #fff; --canvas: #f6f7fb; --accent: #635bff; --accent-dark: #5048e5; }
* { box-sizing: border-box; }
body { margin: 0; min-height: 100vh; background: var(--canvas); color: var(--ink); font: 15px/1.45 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.page { width: min(100%, 1040px); margin: 0 auto; padding: 64px 28px; }
.eyebrow { margin: 0 0 8px; color: var(--accent); font-size: 12px; font-weight: 800; letter-spacing: .11em; text-transform: uppercase; }
h1 { margin: 0; font-size: clamp(32px, 5vw, 44px); line-height: 1.1; letter-spacing: -.04em; }
.intro { max-width: 560px; margin: 14px 0 38px; color: var(--muted); font-size: 16px; }
.settings { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 28px; align-items: start; }
.tabs { padding: 12px; border: 1px solid var(--line); border-radius: 16px; background: var(--surface); }
.tab { display: flex; width: 100%; align-items: center; gap: 10px; padding: 11px 12px; border: 0; border-radius: 9px; background: transparent; color: #5d6676; cursor: pointer; font: inherit; font-weight: 650; text-align: left; }
.tab:hover { background: #f6f6ff; color: var(--accent); }
.tab.active { background: #eeedff; color: var(--accent); }
.panel { overflow: hidden; border: 1px solid var(--line); border-radius: 16px; background: var(--surface); box-shadow: 0 10px 32px rgba(33, 39, 57, .05); }
.section { padding: 30px 32px; border-bottom: 1px solid var(--line); }
.section:last-of-type { border-bottom: 0; }
h2 { margin: 0; font-size: 18px; letter-spacing: -.02em; }
.section-copy { margin: 6px 0 24px; color: var(--muted); }
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
label { display: block; color: #394254; font-size: 13px; font-weight: 750; }
input, select { width: 100%; height: 44px; margin-top: 8px; padding: 0 12px; border: 1px solid #dce0e8; border-radius: 8px; outline: none; background: #fff; color: var(--ink); font: inherit; transition: border-color .15s, box-shadow .15s; }
input:focus, select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(99, 91, 255, .13); }
.row { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 17px 0; border-top: 1px solid var(--line); }
.row:first-of-type { padding-top: 0; border-top: 0; }
.row:last-child { padding-bottom: 0; }
.row-title { display: block; font-weight: 750; color: #30394a; }
.row-copy { display: block; max-width: 460px; margin-top: 3px; color: var(--muted); font-size: 13px; }
.switch { position: relative; display: inline-flex; flex: 0 0 auto; width: 46px; height: 26px; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.slider { width: 46px; height: 26px; border-radius: 999px; background: #c9ced8; transition: .2s; }
.slider::after { display: block; width: 20px; height: 20px; margin: 3px; border-radius: 50%; background: white; box-shadow: 0 1px 3px rgba(0,0,0,.18); content: ""; transition: .2s; }
.switch input:checked + .slider { background: var(--accent); }
.switch input:checked + .slider::after { transform: translateX(20px); }
.actions { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 22px 32px; background: #fcfcfe; }
.status { color: #15803d; font-size: 13px; font-weight: 650; opacity: 0; transition: opacity .2s; }
.status.show { opacity: 1; }
button { font: inherit; font-weight: 750; cursor: pointer; }
.button-group { display: flex; gap: 10px; }
.button { min-height: 42px; padding: 0 16px; border: 1px solid #dce0e8; border-radius: 8px; background: #fff; color: #3b4454; }
.button.primary { border-color: var(--accent); background: var(--accent); color: white; box-shadow: 0 5px 11px rgba(99,91,255,.2); }
.button.primary:hover { background: var(--accent-dark); }
@media (max-width: 720px) { .page { padding: 38px 18px; } .settings { grid-template-columns: 1fr; } .tabs { display: flex; overflow-x: auto; padding: 8px; } .tab { width: auto; white-space: nowrap; } .section, .actions { padding-left: 20px; padding-right: 20px; } .field-grid { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<main class="page">
<p class="eyebrow">Workspace</p>
<h1>Settings</h1>
<p class="intro">Manage your profile, preferences, and notifications from one place.</p>
<div class="settings">
<nav class="tabs" aria-label="Settings categories">
<button class="tab active" type="button">Profile</button>
<button class="tab" type="button">Preferences</button>
<button class="tab" type="button">Notifications</button>
<button class="tab" type="button">Security</button>
</nav>
<form class="panel" id="settings-form">
<section class="section">
<h2>Profile information</h2>
<p class="section-copy">These details appear across your workspace.</p>
<div class="field-grid">
<label>Full name<input name="name" value="Alex Morgan" autocomplete="name" /></label>
<label>Email address<input name="email" type="email" value="alex@example.com" autocomplete="email" /></label>
<label>Role<select name="role"><option>Product designer</option><option>Product manager</option><option>Engineer</option><option>Operations</option></select></label>
<label>Time zone<select name="timezone"><option>(GMT+05:30) India Standard Time</option><option>(GMT+00:00) Coordinated Universal Time</option><option>(GMT-05:00) Eastern Time</option></select></label>
</div>
</section>
<section class="section">
<h2>Preferences</h2>
<p class="section-copy">Choose how the workspace works for you.</p>
<div class="row"><div><span class="row-title">Compact layout</span><span class="row-copy">Reduce spacing in lists and navigation.</span></div><label class="switch" aria-label="Compact layout"><input name="compact" type="checkbox" /><span class="slider"></span></label></div>
<div class="row"><div><span class="row-title">Weekly summary</span><span class="row-copy">Receive a recap of your workspace activity every Monday.</span></div><label class="switch" aria-label="Weekly summary"><input name="summary" type="checkbox" checked /><span class="slider"></span></label></div>
<div class="row"><div><span class="row-title">Product updates</span><span class="row-copy">Get occasional emails about features and improvements.</span></div><label class="switch" aria-label="Product updates"><input name="updates" type="checkbox" checked /><span class="slider"></span></label></div>
</section>
<footer class="actions"><span class="status" id="status" role="status">Settings saved</span><div class="button-group"><button class="button" type="reset">Reset</button><button class="button primary" type="submit">Save changes</button></div></footer>
</form>
</div>
</main>
<script>
const form = document.querySelector('#settings-form');
const status = document.querySelector('#status');
form.addEventListener('submit', (event) => { event.preventDefault(); status.classList.add('show'); window.setTimeout(() => status.classList.remove('show'), 2500); });
</script>
</body>
</html>
36 changes: 36 additions & 0 deletions Round 2 - Detailed prompt/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Settings</title>
<link rel="stylesheet" href="settings.css" />
</head>
<body>
<main class="settings-page">
<section class="settings-card" aria-labelledby="settings-title">
<h1 id="settings-title">Settings</h1>
<p class="settings-description">Update your profile details.</p>

<form id="settings-form" novalidate>
<div class="form-field">
<label for="name">Name</label>
<input id="name" name="name" type="text" placeholder="Enter your full name" autocomplete="name" aria-describedby="name-error" />
<p id="name-error" class="field-error" role="alert"></p>
</div>

<div class="form-field">
<label for="email">Email</label>
<input id="email" name="email" type="email" placeholder="you@example.com" autocomplete="email" aria-describedby="email-error" />
<p id="email-error" class="field-error" role="alert"></p>
</div>

<button type="submit">Save</button>
<p id="form-success" class="success-message" role="status" aria-live="polite"></p>
</form>
</section>
</main>

<script type="module" src="settings.js"></script>
</body>
</html>
94 changes: 94 additions & 0 deletions Round 2 - Detailed prompt/settings.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
:root {
font-family: Arial, sans-serif;
color: #1f2937;
background: #f3f4f6;
}

body {
margin: 0;
}

.settings-page {
display: grid;
min-height: 100vh;
place-items: center;
padding: 1.5rem;
}

.settings-card {
width: min(100%, 28rem);
padding: 2rem;
border-radius: 0.75rem;
background: #fff;
box-shadow: 0 8px 24px rgb(31 41 55 / 12%);
}

h1 {
margin: 0;
}

.settings-description {
margin: 0.5rem 0 1.5rem;
color: #4b5563;
}

.form-field {
margin-bottom: 1.25rem;
}

label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}

input {
box-sizing: border-box;
width: 100%;
padding: 0.7rem 0.75rem;
border: 1px solid #9ca3af;
border-radius: 0.375rem;
font: inherit;
}

input:focus {
outline: 3px solid #bfdbfe;
border-color: #2563eb;
}

input[aria-invalid="true"] {
border-color: #b91c1c;
}

.field-error {
min-height: 1.25rem;
margin: 0.35rem 0 0;
color: #b91c1c;
font-size: 0.875rem;
}

button {
padding: 0.7rem 1.1rem;
border: 0;
border-radius: 0.375rem;
background: #2563eb;
color: #fff;
cursor: pointer;
font: inherit;
font-weight: 600;
}

button:hover {
background: #1d4ed8;
}

button:focus-visible {
outline: 3px solid #93c5fd;
outline-offset: 2px;
}

.success-message {
min-height: 1.25rem;
margin: 1rem 0 0;
color: #047857;
}
26 changes: 26 additions & 0 deletions Round 2 - Detailed prompt/settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getSettingsSubmitResult } from "./validation.mjs";

const form = document.querySelector("#settings-form");
const nameInput = document.querySelector("#name");
const emailInput = document.querySelector("#email");
const nameError = document.querySelector("#name-error");
const emailError = document.querySelector("#email-error");
const successMessage = document.querySelector("#form-success");

function showError(input, errorElement, message) {
input.setAttribute("aria-invalid", String(Boolean(message)));
errorElement.textContent = message || "";
}

form.addEventListener("submit", (event) => {
event.preventDefault();
const result = getSettingsSubmitResult({ name: nameInput.value, email: emailInput.value });

showError(nameInput, nameError, result.errors.name);
showError(emailInput, emailError, result.errors.email);
successMessage.textContent = result.submitted ? "Settings saved successfully." : "";

if (!result.submitted) {
(result.errors.name ? nameInput : emailInput).focus();
}
});
23 changes: 23 additions & 0 deletions Round 2 - Detailed prompt/tests/validation.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import test from "node:test";
import assert from "node:assert/strict";
import { getSettingsSubmitResult, validateSettings } from "../validation.mjs";

test("requires a name", () => {
assert.deepEqual(validateSettings({ name: " ", email: "alex@example.com" }), { name: "Name is required." });
});

test("requires an email", () => {
assert.deepEqual(validateSettings({ name: "Alex", email: "" }), { email: "Email is required." });
});

test("rejects an invalid email", () => {
assert.deepEqual(validateSettings({ name: "Alex", email: "not-an-email" }), { email: "Enter a valid email address." });
});

test("does not submit when validation fails", () => {
assert.equal(getSettingsSubmitResult({ name: "", email: "bad" }).submitted, false);
});

test("submits valid settings without errors", () => {
assert.deepEqual(getSettingsSubmitResult({ name: "Alex Johnson", email: "alex@example.com" }), { errors: {}, submitted: true });
});
26 changes: 26 additions & 0 deletions Round 2 - Detailed prompt/validation.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

export function validateSettings({ name, email }) {
const errors = {};

if (!name.trim()) {
errors.name = "Name is required.";
}

if (!email.trim()) {
errors.email = "Email is required.";
} else if (!EMAIL_PATTERN.test(email.trim())) {
errors.email = "Enter a valid email address.";
}

return errors;
}

export function getSettingsSubmitResult(values) {
const errors = validateSettings(values);

return {
errors,
submitted: Object.keys(errors).length === 0,
};
}