Dirty State

Input has changed since loading/saving.

html

<label class="ds" for="ds-1">
  <span class="ds__label">
    Display name
    <span class="ds__flag ds__flag--dot" style="--accent:#E8A020;">Edited</span>
  </span>
  <input class="ds__input" id="ds-1" type="text" value="Steven J." style="--accent:#E8A020;">
</label>
<style>
.ds { display: block; font: 14px system-ui, sans-serif; color: #241430; max-width: 260px; }
.ds__label { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; font-weight: 500; }
.ds__flag { font-size: 11px; font-weight: 600; color: var(--accent); }
.ds__flag--dot::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); margin-right: 4px; }
.ds__flag--badge { padding: 2px 6px; border-radius: 999px; background: color-mix(in srgb, var(--accent) 25%, transparent); }
.ds__input { width: 100%; box-sizing: border-box; padding: 10px 12px; font: inherit; color: #241430; background: #FAF7F2; border: 1.5px solid var(--accent); border-radius: 8px; }
.ds__input:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent); }
</style>

tailwind

<label class="block max-w-[260px] font-sans text-sm text-[#241430]" for="ds-tw-1">
  <span class="flex items-center gap-1.5 mb-1.5 font-medium">
    Display name
    <span class="ds-tw-flag ds-tw-flag--dot text-[11px] font-semibold" style="color:#E8A020;">Edited</span>
  </span>
  <input id="ds-tw-1" type="text" value="Steven J." class="w-full box-border px-3 py-2.5 bg-[#FAF7F2] border-[1.5px] rounded-lg focus-visible:outline-none" style="border-color:#E8A020;">
</label>
<style>
.ds-tw-flag--dot::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: currentColor; margin-right: 4px; }
.ds-tw-flag--badge { padding: 2px 6px; border-radius: 999px; background: color-mix(in srgb, currentColor 25%, transparent); }
</style>

react

export function DirtyState({
  label = "Display name",
  value = "Steven J.",
  dirty = true,
  indicator = "dot",
  accent = "#E8A020",
}) {
  return (
    <label style={{ display: "block", maxWidth: 260, fontFamily: "system-ui, sans-serif", fontSize: 14, color: "#241430" }}>
      <span style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 6, fontWeight: 500 }}>
        {label}
        {dirty && (
          <span style={{
            fontSize: 11, fontWeight: 600, color: accent,
            padding: indicator === "badge" ? "2px 6px" : 0,
            borderRadius: indicator === "badge" ? 999 : 0,
            background: indicator === "badge" ? `color-mix(in srgb, ${accent} 25%, transparent)` : "none",
          }}>
            {indicator === "dot" && <span style={{ display: "inline-block", width: 6, height: 6, borderRadius: "50%", background: accent, marginRight: 4 }} />}
            Edited
          </span>
        )}
      </span>
      <input type="text" defaultValue={value} style={{
        width: "100%", boxSizing: "border-box", padding: "10px 12px", font: "inherit", color: "#241430",
        background: "#FAF7F2", border: `1.5px solid ${accent}`, borderRadius: 8,
      }} />
    </label>
  );
}