copy: remove second-person pronouns, use imperative tone

Replace "you/your" with impersonal alternatives across all
user-facing strings in templates, CLI output, error messages,
and documentation. Add copy style rule to CLAUDE.md.
This commit is contained in:
Jon Seager 2026-02-08 09:50:49 +00:00
parent 3f672d7c06
commit ba319e3ab7
No known key found for this signature in database
18 changed files with 54 additions and 46 deletions

View file

@ -693,6 +693,14 @@ Server-side via `q` query parameter. `ListQuery` extracts it, repos apply `LIKE`
- Prefer `if`/`else` and `switch` over ternaries; never nest ternaries
- **Inline `onclick` + global arrow functions** for pages with imperative JS — do not use `DOMContentLoaded` + `addEventListener`
### Copy & UI Text
- **No second-person pronouns** — never use "you", "your", "you're" in user-facing strings (templates, CLI output, error messages, docs). Use imperative tone or impersonal phrasing instead.
- "Browse all your coffee data" → "Browse all coffee data"
- "Lets you sign in" → "Enables sign-in"
- "Your browser does not support" → "This browser does not support"
- "you won't see it again" → "it will not be shown again"
### Naming & Conventions
- **Method naming**: `order_clause()` for sort query builders (not `sort_clause`)

View file

@ -46,8 +46,8 @@ No users found. Register the first user at:
This link expires in 1 hour.
```
Open that URL, choose a display name, and register a passkey. This creates your account and
signs you in.
Open that URL, choose a display name, and register a passkey. This creates an account and
signs in automatically.
### CLI Authentication
@ -55,12 +55,12 @@ To use the CLI or API for write operations, create a token via browser handoff:
```bash
brewlog token create --name "my-cli-token"
# Browser opens → authenticate with your passkey → token printed once
# Browser opens → authenticate with a passkey → token printed once
export BREWLOG_URL="http://localhost:3000"
export BREWLOG_TOKEN="<token from above>"
# Now you can create data from the CLI
# Create data from the CLI
brewlog roaster add --name "Radical Roasters" --country "United Kingdom"
```
@ -75,7 +75,7 @@ directory is loaded automatically via [dotenvy](https://crates.io/crates/dotenvy
| Variable | Purpose | Default |
| ------------------------ | ------------------------------------------------------ | --------------------- |
| `BREWLOG_RP_ID` | WebAuthn Relying Party ID (your domain) | **required** |
| `BREWLOG_RP_ID` | WebAuthn Relying Party ID (server domain) | **required** |
| `BREWLOG_RP_ORIGIN` | WebAuthn Relying Party origin (full URL) | **required** |
| `BREWLOG_DATABASE_URL` | Database connection string | `sqlite://brewlog.db` |
| `BREWLOG_BIND_ADDRESS` | Server bind address | `127.0.0.1:3000` |

View file

@ -87,14 +87,14 @@ async fn run_server(command: ServeCommand) -> Result<()> {
let openrouter_api_key = command.openrouter_api_key.ok_or_else(|| {
anyhow::anyhow!(
"BREWLOG_OPENROUTER_API_KEY is required. Set this environment variable \
to your OpenRouter API key for AI-powered extraction features."
to an OpenRouter API key for AI-powered extraction features."
)
})?;
let foursquare_api_key = command.foursquare_api_key.ok_or_else(|| {
anyhow::anyhow!(
"BREWLOG_FOURSQUARE_API_KEY is required. Set this environment variable \
to your Foursquare API key for nearby cafe search."
to a Foursquare API key for nearby cafe search."
)
})?;

View file

@ -102,7 +102,7 @@ pub async fn create_token(client: &BrewlogClient, cmd: CreateTokenCommand) -> Re
println!("Token Name: {}", cmd.name);
println!("\nSave this token securely - it will not be shown again:");
println!("\n{token}");
println!("\nExport it in your environment:");
println!("\nExport it as an environment variable:");
println!(" export BREWLOG_TOKEN={token}");
Ok(())
@ -159,7 +159,7 @@ async fn run_callback_server(
let _ = sender.send(token);
}
Html("<html><body><h1>Authenticated</h1><p>You can close this window and return to the terminal.</p></body></html>".to_string())
Html("<html><body><h1>Authenticated</h1><p>This window can be closed. Return to the terminal.</p></body></html>".to_string())
}
}),
);

View file

@ -266,8 +266,8 @@
</div>
{% else if grinder_options.is_empty() || brewer_options.is_empty() %}
<div class="text-sm text-text-secondary">
<h3 class="text-lg font-semibold text-text">Add your gear first</h3>
<p class="mt-2">Brews need a grinder and brewer. Add your gear to enable this form.</p>
<h3 class="text-lg font-semibold text-text">Add gear first</h3>
<p class="mt-2">Brews need a grinder and brewer. Add gear to enable this form.</p>
</div>
{% else %}
<div>
@ -449,7 +449,7 @@
<div data-show="$_addType === 'gear'" style="display:none" class="rounded-lg border bg-surface p-5">
<div>
<h3 class="text-lg font-semibold text-text">New Gear</h3>
<p class="mt-1 text-sm text-text-secondary">Add brewing equipment to your collection.</p>
<p class="mt-1 text-sm text-text-secondary">Add brewing equipment.</p>
</div>
<form method="post" action="/api/v1/gear" class="mt-4 flex flex-col gap-4">
<div class="grid gap-4 sm:grid-cols-3">
@ -550,7 +550,7 @@
{% else %}
<div>
<h3 class="text-lg font-semibold text-text">New Cup</h3>
<p class="mt-1 text-sm text-text-secondary">Record a coffee you had at a cafe.</p>
<p class="mt-1 text-sm text-text-secondary">Record a coffee from a cafe visit.</p>
</div>
<form method="post" action="/api/v1/cups" class="mt-4 flex flex-col gap-4">
<div class="grid gap-4 sm:grid-cols-2">

View file

@ -6,7 +6,7 @@
{% block content %}
<header class="flex flex-col gap-2">
<h1 class="text-3xl font-semibold">Admin</h1>
<p class="max-w-2xl text-sm text-text-secondary">Manage your passkeys, API tokens, and data.</p>
<p class="max-w-2xl text-sm text-text-secondary">Manage passkeys, API tokens, and data.</p>
</header>
<!-- Passkeys -->
@ -14,7 +14,7 @@
<div class="flex flex-col gap-4">
<div>
<h2 class="text-lg font-semibold text-text">Passkeys</h2>
<p class="mt-1 text-sm text-text-secondary">Passkeys let you sign in securely without a password.</p>
<p class="mt-1 text-sm text-text-secondary">Passkeys enable secure sign-in without a password.</p>
</div>
<div class="flex flex-col gap-2">
@ -45,7 +45,7 @@
</div>
{% if passkeys.len() <= 1 %}
<p class="text-xs text-text-muted">Add another passkey before removing your only one.</p>
<p class="text-xs text-text-muted">Add another passkey before removing the only one.</p>
{% endif %}
<!-- Add passkey form -->
@ -81,7 +81,7 @@
</div>
<div id="add-passkey-loading" class="mt-3 hidden flex items-center gap-3 text-sm text-accent">
{{ icons::spinner("h-5 w-5") }}
Follow the prompts from your browser or device...
Follow the prompts from the browser or device...
</div>
</div>
@ -195,7 +195,7 @@
>
{{ icons::x_mark("h-4 w-4") }}
</button>
<p class="pr-6 text-sm font-medium text-green-800 dark:text-green-300">Token created! Copy it now — you won't see it again.</p>
<p class="pr-6 text-sm font-medium text-green-800 dark:text-green-300">Token created! Copy it now — it will not be shown again.</p>
<div class="mt-3 flex items-center gap-2">
<code id="token-value" data-text="$_tokenValue"
class="flex-1 rounded bg-surface px-3 py-2 text-sm font-mono text-text border border-green-200 dark:border-green-800 break-all select-all"></code>
@ -402,7 +402,7 @@
return;
}
if (!confirm("Are you sure? All coffee data will be permanently deleted.")) {
if (!confirm("Confirm? All coffee data will be permanently deleted.")) {
return;
}
@ -434,7 +434,7 @@
if (response.ok) {
window.location.reload();
} else if (response.status === 409) {
alert("Cannot delete your only passkey. Add another passkey first.");
alert("Cannot delete the only passkey. Add another passkey first.");
} else {
alert("Failed to delete passkey.");
}

View file

@ -230,11 +230,11 @@ block title %}Brewlog · Check In{% endblock %} {% block head %}
<!-- Step 2: Roast identification -->
<div class="mt-4" data-show="$_step === 2" style="display: none">
<div class="rounded-lg border bg-surface p-5">
<h3 class="text-base font-semibold text-text mb-3">What are you drinking?</h3>
<h3 class="text-base font-semibold text-text mb-3">Select a coffee</h3>
<div data-show="!$_scanSuccess" class="mb-4">
<p class="text-sm text-text-secondary mb-3" data-show="!$_extracting">
Scan a bag to identify the coffee, or select from your existing roasts below.
Scan a bag to identify the coffee, or select from existing roasts below.
</p>
<form

View file

@ -2,7 +2,7 @@
{% block content %}
<header class="flex flex-col gap-2">
<h1 class="text-3xl font-semibold">Data</h1>
<p class="max-w-2xl text-sm text-text-secondary">Browse all your coffee data.</p>
<p class="max-w-2xl text-sm text-text-secondary">Browse all coffee data.</p>
</header>
<div class="flex flex-col gap-4">

View file

@ -8,13 +8,13 @@
<div class="rounded-lg border bg-surface p-6">
<h1 class="text-2xl font-semibold text-accent">Login</h1>
<p class="mt-2 text-sm text-text-secondary">
Sign in with your passkey to manage your coffee log.
Sign in with a passkey to access the coffee log.
</p>
<div id="login-error" class="mt-4 hidden rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800"></div>
<div id="login-unsupported" class="mt-4 hidden rounded-md bg-yellow-100 border border-yellow-300 p-3 text-sm text-yellow-800">
Your browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
This browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
</div>
<div class="mt-6">

View file

@ -8,13 +8,13 @@
<div class="rounded-lg border bg-surface p-6">
<h1 class="text-2xl font-semibold text-accent">Register</h1>
<p class="mt-2 text-sm text-text-secondary">
Create your account by registering a passkey. This will be used to sign in going forward.
Create an account by registering a passkey. This will be used to sign in going forward.
</p>
<div id="register-error" class="mt-4 hidden rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800"></div>
<div id="register-unsupported" class="mt-4 hidden rounded-md bg-yellow-100 border border-yellow-300 p-3 text-sm text-yellow-800">
Your browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
This browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
</div>
<div id="register-form" class="mt-6 flex flex-col gap-4">
@ -26,7 +26,7 @@
required
autofocus
class="input-field"
placeholder="Your name"
placeholder="Display name"
/>
</label>
@ -49,14 +49,14 @@
</button>
<div id="register-loading" class="mt-2 hidden text-center text-sm text-text-muted">
<p>Follow the prompts from your browser or device...</p>
<p>Follow the prompts from the browser or device...</p>
</div>
</div>
<div id="register-success" class="mt-6 hidden">
<div class="rounded-md bg-green-100 border border-green-300 p-4 text-sm text-green-800">
<p class="font-semibold">Registration successful!</p>
<p class="mt-1">Your passkey has been registered and you are now signed in.</p>
<p class="mt-1">Passkey registered. Now signed in.</p>
</div>
<a
href="/"

View file

@ -8,7 +8,7 @@
>
<p class="text-center">
{% if is_authenticated %}
No bags added yet. Click the Add button to create your first bag.
No bags added yet. Click Add to create the first bag.
{% else %}
No bags added yet.
{% endif %}
@ -135,7 +135,7 @@
</table>
</div>
{% if bags.items.is_empty() %}
<div class="p-8 text-center text-text-muted">No bags match your search.</div>
<div class="p-8 text-center text-text-muted">No bags match the search.</div>
{% endif %}
{{ table::pagination_header(bags, navigator, "#bag-list") }}
{% if bags.has_next() %}

View file

@ -22,7 +22,7 @@
>
<p class="text-center">
{% if is_authenticated %}
No brews logged yet. Click the Add button to log your first brew.
No brews logged yet. Click Add to log the first brew.
{% else %}
No brews logged yet.
{% endif %}
@ -124,7 +124,7 @@
{% if brews.items.is_empty() %}
<div class="p-8 text-center text-text-muted">
No brews match your search.
No brews match the search.
</div>
{% endif %}

View file

@ -8,7 +8,7 @@
>
<p class="text-center">
{% if is_authenticated %}
No cafes added yet. Click the Add button to add your first cafe.
No cafes added yet. Click Add to add the first cafe.
{% else %}
No cafes added yet.
{% endif %}
@ -110,7 +110,7 @@
</table>
</div>
{% if cafes.items.is_empty() %}
<div class="p-8 text-center text-text-muted">No cafes match your search.</div>
<div class="p-8 text-center text-text-muted">No cafes match the search.</div>
{% endif %}
{{ table::pagination_header(cafes, navigator, "#cafe-list") }}
{% if cafes.has_next() %}

View file

@ -8,7 +8,7 @@
>
<p class="text-center">
{% if is_authenticated %}
No cups added yet. Click the Add button to add your first cup.
No cups added yet. Click Add to add the first cup.
{% else %}
No cups added yet.
{% endif %}
@ -93,7 +93,7 @@
</table>
</div>
{% if cups.items.is_empty() %}
<div class="p-8 text-center text-text-muted">No cups match your search.</div>
<div class="p-8 text-center text-text-muted">No cups match the search.</div>
{% endif %}
{{ table::pagination_header(cups, navigator, "#cup-list") }}
{% if cups.has_next() %}

View file

@ -8,7 +8,7 @@
>
<p class="text-center">
{% if is_authenticated %}
No gear added yet. Click the Add button to add your first piece of equipment.
No gear added yet. Click Add to add the first piece of equipment.
{% else %}
No gear added yet.
{% endif %}
@ -90,7 +90,7 @@
</table>
</div>
{% if gear.items.is_empty() %}
<div class="p-8 text-center text-text-muted">No gear matches your search.</div>
<div class="p-8 text-center text-text-muted">No gear matches the search.</div>
{% endif %}
{{ table::pagination_header(gear, navigator, "#gear-list") }}
{% if gear.has_next() %}

View file

@ -8,7 +8,7 @@
>
<p class="text-center">
{% if is_authenticated %}
No roasts added yet. Click the Add button to create your first roast.
No roasts added yet. Click Add to create the first roast.
{% else %}
No roasts added yet.
{% endif %}
@ -111,7 +111,7 @@
</table>
</div>
{% if roasts.items.is_empty() %}
<div class="p-8 text-center text-text-muted">No roasts match your search.</div>
<div class="p-8 text-center text-text-muted">No roasts match the search.</div>
{% endif %}
{{ table::pagination_header(roasts, navigator, "#roast-list") }}
{% if roasts.has_next() %}

View file

@ -8,7 +8,7 @@
>
<p class="text-center">
{% if is_authenticated %}
No roasters added yet. Click the Add button to create your first roaster.
No roasters added yet. Click Add to create the first roaster.
{% else %}
No roasters added yet.
{% endif %}
@ -110,7 +110,7 @@
</table>
</div>
{% if roasters.items.is_empty() %}
<div class="p-8 text-center text-text-muted">No roasters match your search.</div>
<div class="p-8 text-center text-text-muted">No roasters match the search.</div>
{% endif %}
{{ table::pagination_header(roasters, navigator, "#roaster-list") }}
{% if roasters.has_next() %}

View file

@ -7,7 +7,7 @@ const locateUser = (triggerEl) => {
root.dispatchEvent(new CustomEvent(name, { detail, bubbles: true }));
if (!navigator.geolocation) {
emit('location-error', { message: 'Geolocation not supported by your browser.' });
emit('location-error', { message: 'Geolocation not supported by this browser.' });
return;
}
@ -48,7 +48,7 @@ const locateUser = (triggerEl) => {
</button>
<input type="text" data-bind:_city-name
class="input-field w-full"
data-attr:placeholder="$_locationFound ? 'Using your location (' + $_userLat.toFixed(2) + ', ' + $_userLng.toFixed(2) + ')' : 'Name of city or area'"
data-attr:placeholder="$_locationFound ? 'Using current location (' + $_userLat.toFixed(2) + ', ' + $_userLng.toFixed(2) + ')' : 'Name of city or area'"
data-attr:disabled="$_locationFound"
data-on:input__debounce.350ms="$_cityName.length >= 2 && $_cafeSearch.length >= 2 && @get('/api/v1/nearby-cafes?near=' + encodeURIComponent($_cityName) + '&q=' + encodeURIComponent($_cafeSearch), {responseOverrides: {selector: '#nearby-results', mode: 'replace'}})"
/>