{
 "openapi": "3.1.0",
 "info": {
  "title": "Swatch API",
  "version": "1.0.0",
  "description": "Swatch checks text and background color contrast against WCAG, shows the closest colors that pass, and builds a full 50-950 palette from any color. Runs in your browser, no account. Public, no API key. Rate limited. By Clawhard Studio, an AI-powered studio.",
  "contact": {
   "name": "Clawhard Studio",
   "url": "https://clawhard.com",
   "email": "support@clawhard.com"
  }
 },
 "servers": [
  {
   "url": "https://swatch.clawhard.com"
  }
 ],
 "paths": {
  "/api/v1/check_contrast": {
   "get": {
    "operationId": "check_contrast",
    "summary": "Check text and background color contrast (WCAG and APCA)",
    "description": "Use when you pick, review or generate UI colors and need an exact accessibility answer. Computes the WCAG 2.x contrast ratio of a text color on a background (never guess ratios, LLMs get them wrong) and returns pass or fail for AA and AAA at normal and large text, the 3:1 UI-component rule, and…",
    "x-full-description": "Use when you pick, review or generate UI colors and need an exact accessibility answer. Computes the WCAG 2.x contrast ratio of a text color on a background (never guess ratios, LLMs get them wrong) and returns pass or fail for AA and AAA at normal and large text, the 3:1 UI-component rule, and the APCA Lc score. Accepts hex (#rgb, #rrggbb, #rrggbbaa), rgb(), hsl(), hwb(), oklch() and CSS color names. Translucent colors are blended first. Returns: {summary, foreground, background, ratio, ratio_text, grade (\"AAA\"|\"AA\"|\"AA Large\"|\"Fail\"), aa_text, aa_large_text, aaa_text, aaa_large_text, ui_components, apca_lc, apca_note, open_url}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "parameters": [
     {
      "name": "foreground",
      "in": "query",
      "required": true,
      "description": "Text (or icon) color, e.g. \"#767676\" or \"rgb(20 20 20)\" or \"tomato\".",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "background",
      "in": "query",
      "required": true,
      "description": "Background color, e.g. \"#ffffff\".",
      "schema": {
       "type": "string"
      }
     }
    ]
   },
   "post": {
    "operationId": "check_contrast_post",
    "summary": "Check text and background color contrast (WCAG and APCA)",
    "description": "Use when you pick, review or generate UI colors and need an exact accessibility answer. Computes the WCAG 2.x contrast ratio of a text color on a background (never guess ratios, LLMs get them wrong) and returns pass or fail for AA and AAA at normal and large text, the 3:1 UI-component rule, and…",
    "x-full-description": "Use when you pick, review or generate UI colors and need an exact accessibility answer. Computes the WCAG 2.x contrast ratio of a text color on a background (never guess ratios, LLMs get them wrong) and returns pass or fail for AA and AAA at normal and large text, the 3:1 UI-component rule, and the APCA Lc score. Accepts hex (#rgb, #rrggbb, #rrggbbaa), rgb(), hsl(), hwb(), oklch() and CSS color names. Translucent colors are blended first. Returns: {summary, foreground, background, ratio, ratio_text, grade (\"AAA\"|\"AA\"|\"AA Large\"|\"Fail\"), aa_text, aa_large_text, aaa_text, aaa_large_text, ui_components, apca_lc, apca_note, open_url}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "foreground": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "Text (or icon) color, e.g. \"#767676\" or \"rgb(20 20 20)\" or \"tomato\"."
         },
         "background": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "Background color, e.g. \"#ffffff\"."
         }
        },
        "required": [
         "foreground",
         "background"
        ]
       }
      }
     }
    }
   }
  },
  "/api/v1/fix_contrast": {
   "get": {
    "operationId": "fix_contrast",
    "summary": "Find the closest colors that pass a contrast target",
    "description": "Use when a color pair fails contrast and you need a replacement that still looks like the original. Returns the nearest passing text color (background fixed) and the nearest passing background (text fixed), keeping hue and changing only lightness, with the new exact ratios.",
    "x-full-description": "Use when a color pair fails contrast and you need a replacement that still looks like the original. Returns the nearest passing text color (background fixed) and the nearest passing background (text fixed), keeping hue and changing only lightness, with the new exact ratios. Targets: AA (4.5:1 normal text, default), AAA (7:1), AA-large (3:1 large text), ui (3:1 icons and borders). If the pair already passes it says so. Returns: {summary, target, target_ratio, current_ratio, already_passes, change_text_color:{foreground, ratio, pair}, change_background:{background, ratio, pair}, message, open_url}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "parameters": [
     {
      "name": "foreground",
      "in": "query",
      "required": true,
      "description": "Current text color.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "background",
      "in": "query",
      "required": true,
      "description": "Current background color.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "target",
      "in": "query",
      "required": false,
      "description": "Contrast target. Default AA.",
      "schema": {
       "type": "string",
       "enum": [
        "AA",
        "AAA",
        "AA-large",
        "ui"
       ]
      }
     }
    ]
   },
   "post": {
    "operationId": "fix_contrast_post",
    "summary": "Find the closest colors that pass a contrast target",
    "description": "Use when a color pair fails contrast and you need a replacement that still looks like the original. Returns the nearest passing text color (background fixed) and the nearest passing background (text fixed), keeping hue and changing only lightness, with the new exact ratios.",
    "x-full-description": "Use when a color pair fails contrast and you need a replacement that still looks like the original. Returns the nearest passing text color (background fixed) and the nearest passing background (text fixed), keeping hue and changing only lightness, with the new exact ratios. Targets: AA (4.5:1 normal text, default), AAA (7:1), AA-large (3:1 large text), ui (3:1 icons and borders). If the pair already passes it says so. Returns: {summary, target, target_ratio, current_ratio, already_passes, change_text_color:{foreground, ratio, pair}, change_background:{background, ratio, pair}, message, open_url}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "foreground": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "Current text color."
         },
         "background": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "Current background color."
         },
         "target": {
          "type": "string",
          "enum": [
           "AA",
           "AAA",
           "AA-large",
           "ui"
          ],
          "description": "Contrast target. Default AA."
         }
        },
        "required": [
         "foreground",
         "background"
        ]
       }
      }
     }
    }
   }
  },
  "/api/v1/pick_text_color": {
   "get": {
    "operationId": "pick_text_color",
    "summary": "Pick black or white text for a background color",
    "description": "Use when you place text or an icon on a colored surface (button, badge, chip, tag, hero) and need the readable text color. Returns whichever of black or white has the higher contrast ratio, both ratios, the grade, and a softer near-black or near-white option that still passes AA.",
    "x-full-description": "Use when you place text or an icon on a colored surface (button, badge, chip, tag, hero) and need the readable text color. Returns whichever of black or white has the higher contrast ratio, both ratios, the grade, and a softer near-black or near-white option that still passes AA. Returns: {summary, background, best_text_color, ratio, white_ratio, black_ratio, grade, soft_option}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "parameters": [
     {
      "name": "background",
      "in": "query",
      "required": true,
      "description": "The surface color the text sits on.",
      "schema": {
       "type": "string"
      }
     }
    ]
   },
   "post": {
    "operationId": "pick_text_color_post",
    "summary": "Pick black or white text for a background color",
    "description": "Use when you place text or an icon on a colored surface (button, badge, chip, tag, hero) and need the readable text color. Returns whichever of black or white has the higher contrast ratio, both ratios, the grade, and a softer near-black or near-white option that still passes AA.",
    "x-full-description": "Use when you place text or an icon on a colored surface (button, badge, chip, tag, hero) and need the readable text color. Returns whichever of black or white has the higher contrast ratio, both ratios, the grade, and a softer near-black or near-white option that still passes AA. Returns: {summary, background, best_text_color, ratio, white_ratio, black_ratio, grade, soft_option}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "background": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "The surface color the text sits on."
         }
        },
        "required": [
         "background"
        ]
       }
      }
     }
    }
   }
  },
  "/api/v1/make_scale": {
   "get": {
    "operationId": "make_scale",
    "summary": "Build a 50 to 950 color scale from one color",
    "description": "Use when you need a design-token palette from a brand color. Generates an 11-step scale (50, 100 ... 950) in OKLCH so steps look evenly spaced, with your exact color kept as the closest step.",
    "x-full-description": "Use when you need a design-token palette from a brand color. Generates an 11-step scale (50, 100 ... 950) in OKLCH so steps look evenly spaced, with your exact color kept as the closest step. Returns each step's hex, contrast on white and black, the best text color on it, and ready-to-paste CSS variables, a Tailwind colors block and JSON. Returns: {summary, input, name, anchor_step, steps:[{step, hex, on_white, on_black, best_text, is_input}], lightest_step_passing_aa_on_white, css, tailwind, json, open_url}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "parameters": [
     {
      "name": "color",
      "in": "query",
      "required": true,
      "description": "Base color, e.g. \"#ff5a1f\".",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "name",
      "in": "query",
      "required": false,
      "description": "Token name used in the CSS and Tailwind output. Default \"brand\".",
      "schema": {
       "type": "string"
      }
     }
    ]
   },
   "post": {
    "operationId": "make_scale_post",
    "summary": "Build a 50 to 950 color scale from one color",
    "description": "Use when you need a design-token palette from a brand color. Generates an 11-step scale (50, 100 ... 950) in OKLCH so steps look evenly spaced, with your exact color kept as the closest step.",
    "x-full-description": "Use when you need a design-token palette from a brand color. Generates an 11-step scale (50, 100 ... 950) in OKLCH so steps look evenly spaced, with your exact color kept as the closest step. Returns each step's hex, contrast on white and black, the best text color on it, and ready-to-paste CSS variables, a Tailwind colors block and JSON. Returns: {summary, input, name, anchor_step, steps:[{step, hex, on_white, on_black, best_text, is_input}], lightest_step_passing_aa_on_white, css, tailwind, json, open_url}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "color": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "Base color, e.g. \"#ff5a1f\"."
         },
         "name": {
          "type": "string",
          "maxLength": 24,
          "description": "Token name used in the CSS and Tailwind output. Default \"brand\"."
         }
        },
        "required": [
         "color"
        ]
       }
      }
     }
    }
   }
  },
  "/api/v1/convert_color": {
   "get": {
    "operationId": "convert_color",
    "summary": "Convert a color between hex, rgb, hsl and oklch",
    "description": "Use when you need the same color in another CSS format, or a color name turned into a value. Returns hex, rgb(), hsl(), oklch(), relative luminance and the nearest CSS color name. Accepts hex, rgb(), hsl(), hwb(), oklch() and CSS names.",
    "x-full-description": "Use when you need the same color in another CSS format, or a color name turned into a value. Returns hex, rgb(), hsl(), oklch(), relative luminance and the nearest CSS color name. Accepts hex, rgb(), hsl(), hwb(), oklch() and CSS names. Returns: {summary, input, hex, rgb, hsl, oklch, luminance, nearest_name, alpha?}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "parameters": [
     {
      "name": "color",
      "in": "query",
      "required": true,
      "description": "Any CSS color.",
      "schema": {
       "type": "string"
      }
     }
    ]
   },
   "post": {
    "operationId": "convert_color_post",
    "summary": "Convert a color between hex, rgb, hsl and oklch",
    "description": "Use when you need the same color in another CSS format, or a color name turned into a value. Returns hex, rgb(), hsl(), oklch(), relative luminance and the nearest CSS color name. Accepts hex, rgb(), hsl(), hwb(), oklch() and CSS names.",
    "x-full-description": "Use when you need the same color in another CSS format, or a color name turned into a value. Returns hex, rgb(), hsl(), oklch(), relative luminance and the nearest CSS color name. Accepts hex, rgb(), hsl(), hwb(), oklch() and CSS names. Returns: {summary, input, hex, rgb, hsl, oklch, luminance, nearest_name, alpha?}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "color": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80,
          "description": "Any CSS color."
         }
        },
        "required": [
         "color"
        ]
       }
      }
     }
    }
   }
  },
  "/api/v1/check_pairs": {
   "post": {
    "operationId": "check_pairs",
    "summary": "Check many foreground and background pairs at once",
    "description": "Use to audit a whole UI or design-token set in one call. Takes up to 50 pairs and returns each pair's ratio, grade and AA/AAA results, plus how many fail AA for normal text. Ideal after you generate a theme or dark mode and want every text-on-surface pair verified.",
    "x-full-description": "Use to audit a whole UI or design-token set in one call. Takes up to 50 pairs and returns each pair's ratio, grade and AA/AAA results, plus how many fail AA for normal text. Ideal after you generate a theme or dark mode and want every text-on-surface pair verified. Returns: {count, passing_aa_text, failing_aa_text, errors, results:[{label, foreground, background, ratio, grade, aa_text, aa_large_text, aaa_text, ui_components, apca_lc}], summary}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "pairs": {
          "type": "array",
          "minItems": 1,
          "maxItems": 50,
          "description": "Pairs to check.",
          "items": {
           "type": "object",
           "properties": {
            "foreground": {
             "type": "string",
             "minLength": 1,
             "maxLength": 80
            },
            "background": {
             "type": "string",
             "minLength": 1,
             "maxLength": 80
            },
            "label": {
             "type": "string",
             "maxLength": 60,
             "description": "Optional name, like \"muted text on card\"."
            }
           },
           "required": [
            "foreground",
            "background"
           ]
          }
         }
        },
        "required": [
         "pairs"
        ]
       }
      }
     }
    }
   }
  }
 },
 "externalDocs": {
  "description": "llms.txt",
  "url": "https://swatch.clawhard.com/llms.txt"
 }
}