{"id":34107876,"url":"https://github.com/spear-ai/citizen","last_synced_at":"2026-04-01T20:45:43.217Z","repository":{"id":65798866,"uuid":"485442555","full_name":"spear-ai/citizen","owner":"spear-ai","description":"Spear AI Code Citizen","archived":false,"fork":false,"pushed_at":"2026-02-26T20:58:47.000Z","size":1756050,"stargazers_count":9,"open_issues_count":101,"forks_count":1,"subscribers_count":6,"default_branch":"main","last_synced_at":"2026-02-26T21:13:21.516Z","etag":null,"topics":["code","guide","style"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spear-ai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-04-25T16:10:22.000Z","updated_at":"2026-01-27T20:50:41.000Z","dependencies_parsed_at":null,"dependency_job_id":"f976920c-1868-4160-83b0-aa6dec6deb8f","html_url":"https://github.com/spear-ai/citizen","commit_stats":{"total_commits":37,"total_committers":3,"mean_commits":"12.333333333333334","dds":"0.21621621621621623","last_synced_commit":"39e2239333bce71e2a126f67b72e48dc79e552cc"},"previous_names":[],"tags_count":104,"template":false,"template_full_name":null,"purl":"pkg:github/spear-ai/citizen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spear-ai%2Fcitizen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spear-ai%2Fcitizen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spear-ai%2Fcitizen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spear-ai%2Fcitizen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spear-ai","download_url":"https://codeload.github.com/spear-ai/citizen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spear-ai%2Fcitizen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291797,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code","guide","style"],"created_at":"2025-12-14T18:10:33.262Z","updated_at":"2026-04-01T20:45:43.205Z","avatar_url":"https://github.com/spear-ai.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg alt=\"Logo\" src=\"citizen-logo.png\" width=\"384\"\u003e\n  \u003cp\u003e\n    \u003cb\u003eCitizen\u003c/b\u003e\n  \u003c/p\u003e\n  \u003cquote\u003e\n    \u003cp\u003e\n      “This code is what it is because of its citizens” — Plato\n    \u003c/p\u003e\n  \u003c/quote\u003e\n  \u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://github.com/spear-ai/citizen/tree/main/packages/code-commitment\"\u003e\n      \u003cpicture\u003e\n        \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://github.com/spear-ai/citizen/raw/main/packages/code-commitment/gold-badge-dark.png\"\u003e\n        \u003cimg alt=\"Code Commitment — Gold\" src=\"https://github.com/spear-ai/citizen/raw/main/packages/code-commitment/gold-badge-light.png\" height=\"20\" /\u003e\n      \u003c/picture\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/spear-ai/citizen/actions/workflows/check.yaml\"\u003e\n      \u003cimg alt=\"Checks\" src=\"https://github.com/spear-ai/citizen/actions/workflows/check.yaml/badge.svg\"\u003e\n    \u003c/a\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\n# Goal\n\nDecrease net cognitive complexity for developers.\n\n# Assumptions\n\nThe rules in this guide are intended to work well with the following assumptions:\n\n## Read \u0026 Write\n\nDevelopers read code more than they write code, and internal developers write code more than outside collaborators:\n\n- Readability \u003e Writeability\n- Writeability for _internal_ developers \u003e Writeability for outside collaborators\n\n## Screen size\n\nDevelopers have medium–large displays:\n\n- Long lines are less likely to wrap\n\n## GitHub\n\nDevelopers use GitHub:\n\n- Side-by-side line diffs are **119** characters long\n\n## VSCode\n\nDevelopers use VSCode:\n\n- Auto-formatting, auto-linting, and code completion reduces keystrokes\n\n# Legend\n\n[!] Indicates that a rule is controversial.\n\n# Rules\n\n## Language\n\n**TODO:** Fill me out\n\n## Files\n\n**TODO:** Fill me out\n\n## Writing\n\n### ¶ cE.Cx: DO use informal tone\n\nDo use a friendly and informal tone. Other rules follow from this one, such as using contractions.\n\n### ¶ cE.kI: DO use contractions\n\nDo use contractions.\n\n```py\n# ✓ Good\ndef panic():\n    # Panic if we can’t recover from an error.\n```\n\n```py\n# ✗ Bad\ndef panic():\n    # Panic if we cannot recover from an error.\n```\n\n### ¶ cE.j5: DO use curly quotes\n\nDo use curly single and double quotes instead of straight quotes.\n\n```ts\n// ✓ Good\nlet race; // “Xel’Naga”, “Terran”\n```\n\n```ts\n// ✗ Bad\nlet race; // \"Xel'Naga\", \"Terran\"\n```\n\n### ¶ cE.4x: DO use ellipsis instead of dots\n\nDo use the ellipses character instead of 3 dots.\n\n```ts\n// ✓ Good\nlet series; // 1, 2, 3, …, 10\n```\n\n```ts\n// ✗ Bad\nlet series; // 1, 2, 3, ..., 10\n```\n\n### ¶ cE.jF: DO use en dashes instead of hypens\n\nDo use an en dash when appropriate such as for spans and ranges.\n\n```ts\n// ✓ Good\nlet duration; // 2–3 weeks\n```\n\n```ts\n// ✗ Bad\nlet duration; // 2-3 weeks, 2 to 3 weeks\n```\n\n### ¶ cE.Cc: DO use em dashes instead of hypens\n\nDo use an em dash when appropriate such as replacing commas, parentheses, or colons⁠.\n\n```ts\n// ✓ Good\nlet sentence; // Lorem ipsum — foo bar — dolor.\n```\n\n```ts\n// ✗ Bad\nlet sentence; // Lorem ipsum -- foo bar -- dolor.\n```\n\n### ¶ cE.eB: DO use glyphs instead of characters or words\n\n```ts\n// ✓ Good\nlet symbols; // ←, →\nlet angle; // 45°\n```\n\n```ts\n// ✗ Bad\nlet symbols; // \u003c-, -\u003e\nlet angle; // 45 degrees\n```\n\n### ¶ cE.BR: DO use canonical spelling of proper nouns\n\nDo use the canonical spelling of proper nouns⁠.\n\n```py\n# ✓ Good\nwords # Python, Node.js, PostgreSQL\n```\n\n```py\n# ✗ Bad\nwords # python, node, postgres\n```\n\n### ¶ cE.K6: DO begin type descriptions with an indefinite article\n\nDo begin type descriptions with an _indefinite_ article (“a/an”).\n\n**SQL:**\n\n```sql\n-- ✓ Good\nCOMMENT ON TABLE unit IS 'A unit.';\n```\n\n```sql\n-- ✗ Bad\nCOMMENT ON TABLE unit IS 'The unit.';\n```\n\n**GraphQL:**\n\n```graphql\n# ✓ Good\ntype Unit {\n  \"\"\"A unit.\"\"\"\n}\n```\n\n```graphql\n# ✗ Bad\ntype Unit {\n  \"\"\"The unit.\"\"\"\n}\n```\n\n### ¶ cE.re: DO begin type property descriptions with a definite article\n\nDo begin type property descriptions with a _definite_ article (“the”) when applicable.\n\n**SQL:**\n\n```sql\n-- ✓ Good\nCOMMENT ON COLUMN unit.name IS 'The name of a unit.';\n```\n\n```sql\n-- ✗ Bad\nCOMMENT ON COLUMN unit.name IS 'A name of a unit.';\n```\n\n**GraphQL:**\n\n```graphql\n# ✓ Good\ntype Unit {\n  name: String\n  \"\"\"The name of a unit.\"\"\"\n}\n```\n\n```graphql\n# ✗ Bad\ntype Unit {\n  name: String\n  \"\"\"A name of a unit.\"\"\"\n}\n```\n\n**python:**\n\n```py\n# ✓ Good\nclass Unit:\n    name: str  # The name of a unit.\n```\n\n```py\n# ✗ Bad\nclass Unit:\n    name: str  # A name of a unit.\n```\n\n## Comments\n\n### ¶ LW.n8: DO communicate intent\n\nComments should explain intent rather than what the code is doing.\nIf it’s unclear what the code is doing then it should be refactored.\n\n### ¶ LW.8j: DO show examples\n\nDo show examples when possible.\n\n**js:**\n\n```ts\n// Get a unit’s globally unique identifier.\n//\n// (“Zumwalt-class destroyer”, 3) → “Unit:ZumwaltClassDestroyer:3”\n// (“Leopard 2A7”, 5) → “Unit:Leopard2a7:5”\nconst getUnitId = (name: string, index: number) =\u003e `Unit:${pascalCase(name)}:${index}`;\n```\n\n### ¶ LW.pb: DO follow writing styles\n\nDo follow all writing style rules when also writing comments.\n\n```ts\n// ✓ Good\n// e.g. “John Doe” → “john_doe”\nconst toKebabCase = () =\u003e {};\n```\n\n```ts\n// ✗ Bad\n// e.g. \"John Doe\" =\u003e john_doe\nconst toKebabCase = () =\u003e {};\n```\n\n### ¶ LW.hb: DO use sentence case\n\nDo use [sentence case](https://apastyle.apa.org/style-grammar-guidelines/capitalization/sentence-case) in comments.\n\n**py:**\n\n```py\n# ✓ Good\n# Use Manhattan distance because it performs better on high dimensional data\ndistance = scipy.spatial.distance.cdist(tensor_a, tensor_b, metric='cityblock')\n```\n\n```py\n# ✗ Bad\n# use manhattan distance because it performs better on high dimensional data\ndistance = scipy.spatial.distance.cdist(tensor_a, tensor_b, metric='cityblock')\n```\n\n**js:**\n\n```ts\n// ✓ Good\n// Choose a background color that matches the sky\nconst backgroundColor = \"#2ebde5\"; // Azure\n```\n\n```ts\n/* eslint-disable capitalized-comments */\n\n// ✗ Bad\n// choose a background color that matches the sky\nconst backgroundColor = \"#2ebde5\"; // azure\n```\n\n### ¶ LW.nA: DO punctuate block comments\n\nDo use punctuation on block comments.\n\n**py:**\n\n```py\n# ✓ Good\n# Lorem ipsum dolor sit amet.\nplaceholder_text = …\n```\n\n```py\n# ✗ Bad\n# Lorem ipsum dolor sit amet\nplaceholder_text = …\n\n# ✗ Bad\n# Lorem ipsum dolor sit amet\n# Consectetur adipiscing elit\nplaceholder_text = …\n```\n\n### ¶ LW.XU: DO punctuate documentation-as-code\n\nDo punctuate comments that serve as documentation.\nThese comments often generate Website docs, CLI arguments, etc.\n\n```py\n@dataclass\nclass FetchBananasResponse\n    \"\"\"The API response for fetching bananas.\"\"\"\n\n    banana_list: List[Banana]\n    \"\"\"A list of varying length bananas.\"\"\"\n\n@dataclass\nclass Banana:\n    \"\"\"An irresistible fruit loved by all the great apes.\"\"\"\n\n    is_ripe: bool\n    \"\"\"Whether it is ready for eating.\"\"\"\n\n    length: float\n    \"\"\"The length in meters.\"\"\"\n```\n\n### ¶ LW.Sf: DO inline short comments\n\n```py\n# ✓ Good\nspeed = …  # Meters per hour\ntime = …  # Minutes\ndistance = speed / (time / 60)  # Meters\n```\n\n## Variables\n\n### ¶ 9W.1Q. DO follow casing conventions\n\nFollow the casing conventions of the current language.\n\n**json:**\n\n```json\n{\n  \"boundingBox\": [0, 0, 10, 10]\n}\n```\n\n**python:**\n\n```py\nbounding_box = [0, 0, 10, 10]\n```\n\n**rust:**\n\n```rust\nlet bounding_box: [i32; 4] = [0, 0, 10, 10];\n```\n\n**typescript:**\n\n```ts\nconst boundingBox = [0, 0, 10, 10];\n```\n\n### ¶ 9W.MX: DO favor readability to brevity\n\nDo favor readability to brevity.\n\n```py\n# ✓ Good\ndocker_image = \"huggingface/transformers-pytorch-gpu\"\nec2_instance_type = \"p3.8xlarge\"\n```\n\n```py\n# ✗ Bad\nimage = \"huggingface/transformers-pytorch-gpu\"\ntype = \"p3.8xlarge\"\n```\n\n### ¶ 9W.qP: DO group with a prefix\n\nDo group related variables with a prefix.\n\n```py\n# ✓ Good\ncnn_kernel = (3, 3)\ncnn_stride = (1, 1)\nlearning_rate = 1e-05\n```\n\n```py\n# ✗ Bad\nkernel = (3, 3)\nstride = (1, 1)\nlearning_rate = 1e-05\n```\n\n### ¶ 9W.hn: DO space out pre/post-fixes\n\nDo insert a space before postfixes and after prefixes.\n\n```py\n# ✓ Good\nhidden_layer_0 = \"…\"\nhidden_layer_1 = \"…\"\n```\n\n```py\n# ✗ Bad\nhidden_layer0 = \"…\"\nhidden_layer1 = \"…\"\n```\n\n```ts\n// ✓ Good\nconst userName = \"…\";\nconst userPassword = \"…\";\n```\n\n```ts\n// ✗ Bad\nconst username = \"…\";\nconst userPassword = \"…\";\n```\n\n### ¶ 9W.qV: DON’T use abbreviations\n\nDon’t use abbreviations. They’re more likely to encounter naming conflicts.\nThey must also be be learned and memorized.\n\n```py\n# ✓ Good\ndirectory = \"data/units\"\nunit_direction = \"NORTH\"\n```\n\n```py\n# ✗ Bad\ndir = \"data/units\"\nunit_dir = \"NORTH\"\n```\n\n```py\n# ✓ Good\nresult, error = action()\nresponse = request()\n```\n\n```py\n# ✗ Bad\nres, err = action()\nres = req()\n```\n\n```py\n# ✓ Good\nsagemaker = SageMaker()\nhyperparameters = {…}\n```\n\n```py\n# ✗ Bad\nsm = SageMaker()\nhparams = {…}\n```\n\n### ¶ 9W.cv: DO use acronyms\n\nDo use acronyms and initialisms. They’re well known and less verbose.\nMoreover, the words they represent are often unknown; so must be learned and memorized\n\n```py\n# ✓ Good\nnato_classification = \"cosmic\"\nradar_range = 1_700_150  # meters\n```\n\n```py\n# ✗ Bad\nnorth_atlantic_treaty_organization_classification = \"cosmic\"\nradio_detection_and_ranging_range = 1_700_150  # meters\n```\n\n```py\n# ✓ Good\ns3_bucket = \"secret-stuff\"\nwebsite_url = \"https://spear.ai\"\n```\n\n```py\n# ✗ Bad\nsimple_storage_service_bucket = \"secret-stuff\"\nwebsite_uniform_resource_locator = \"https://spear.ai\"\n```\n\n### ¶ 9W.MM: DON’T invent acronyms\n\nDon’t invent acronyms or initialisms _unless_ external users would find it easier to use.\nThey’re _not_ well known; so must be learned and memorized.\n\n```py\n# ✓ Good\naerial_unit_direction = (0.8, 0.45)\naerial_unit_speed = 329\n```\n\n```py\n# ✗ Bad\nau_direction = (0.8, 0.45)\nau_speed = 329\n```\n\n```py\n# ✓ Good\nrcn_has_attention = True\nrcn_hidden_layer_size = 40\n```\n\n```py\n# ✗ Bad\nreally_cool_network_has_attention = True\nreally_cool_network_hidden_layer_size = 40\n```\n\n### ¶ 9W.Co: DO add type hints\n\nDo add type hints. Many names are ambiguous and can be confused for booleans, dates, functions, etc.\n\n**python:**\n\n```py\n# ✓ Good\ncreated_date = \"1776-07-04T04:56:02.000Z\"\nwas_published = True\n```\n\n```py\n# ✗ Bad\ncreated = \"1776-07-04T04:56:02.000Z\"\npublished = True\n```\n\n**typescript:**\n\n```ts\n// ✓ Good\nfileBuffer = await fs.readFile(\"…\");\nisLoaded = false;\n```\n\n```ts\n// ✗ Bad\nfile = await fs.readFile(\"…\");\nloaded = false;\n```\n\n### ¶ 9W.4R: DO differentiate types\n\nDo differentiate types. It’s hard to switch between contexts when variable names represent different types.\n\n```py\n# ✓ Good\nurl = \"https://spear.ai\"\nparsed_url = urlparse(\"https://spear.ai\")\n\n# ✓ Good\nagent_id = \"10\"\nagent = Agent(id=\"10\", type=Tiger)\n```\n\n```py\n# ✗ Bad\nurl = \"https://spear.ai\"\nurl = urlparse(\"https://spear.ai\")\n\n# ✗ Bad\nagent = \"10\"\nagent = Agent(id=\"10\", type=Tiger)\n```\n\n### ¶ 9W.6u: DON’T add type definitions\n\nDon’t add type definitions.\n\nReasons:\n\n- They add superfluous information\n- They increase refactoring costs\n- They decrease portability\n\n```py\n# ✓ Good\nage = 21\nconfidence = 0.9\n```\n\n```py\n# ✗ Bad\nage_int = 21\nconfidence_float = 0.9\n```\n\n```ts\n// ✓ Good\nconst balance = BigInt(7_301_985);\nconst mask = new Uint8Array();\n```\n\n```ts\n// ✗ Bad\nconst bigIntBalance = BigInt(7_301_985);\nconst maskUint8Array = new Uint8Array();\n```\n\n### ¶ 9W.ZX: DO use boolean verbs\n\nDo use appropriate boolean verbs.\n\n```py\n# ✓ Good\ncan_delete = True\nhas_feature = True\nshould_reset = True\n```\n\n```py\n# ✗ Bad\nis_deletable = True\nfeatures = True\nreset = True\n```\n\n### ¶ 9W.6t: DO be positive\n\nDo be positive with boolean variables.\n\n**python:**\n\n```py\n# ✓ Good\nis_enabled = True\nis_visible = False\n```\n\n```py\n# ✗ Bad\nis_disabled = False\nis_not_visible = True\n```\n\n**tsx:**\n\n```tsx\n// ✓ Good\n\u003cTab isActive\u003eClick me\u003c/Tab\u003e\n```\n\n```tsx\n// ✗ Bad\n\u003cTab isInactive={false}\u003eClick me\u003c/Tab\u003e\n```\n\n### ¶ 9W.jt: DO use correct tense\n\nDo use the correct tense.\n\n```py\nif was_suspended and not is_suspended:\n    print(\"Welcome back!\")\n```\n\n### ¶ 9W.fY: DON’T pluralize collections [[!]](#legend)\n\nDon’t pluralize collections. Instead, specify the collection type.\n\nSome object names are already plural. Therefore, it’s impossible to name a collection of those objects.\nMoreover, [Uncountable nouns](https://en.wikipedia.org/wiki/Mass_noun) can’t be pluralized.\n\nSpecifying the collection type also clarifies which operations are allowed.\nFor example, a `List` can be appended or prepended to. Whereas, a `Set` can be added or removed from.\n\n```py\n# ✓ Good\nequipment_list = [{…}, {…}, …, {…}]\nequipment = equipment_list[0]\n\n# ✓ Good\nsettings_map = {\"0\": {…}, \"1\": {…}, …, \"n\": {…}]\nsettings = settings_map[\"0\"]\n\n# ✓ Good\nid_list = [\"0\", \"1\", \"2\", \"0\"]\nid_list.append(\"2\")\nid_set = set(id_list)\n```\n\n```py\n# ✗ Bad\nequipment = [{…}, {…}, …, {…}]\nequipment = equipment[0]\n\n# ✗ Bad\nsettings = {\"0\": {…}, \"1\": {…}, …, \"n\": {…}]\nsettings = settings[\"0\"]\n\n# ✗ Bad\nids = [\"0\", \"1\", \"2\", \"0\"]\nids.append(\"2\")\ndistinct_ids = set(ids)\n```\n\n```console\n# ✓ Good\ncurl https://api.spear.ai/user\ncurl https://api.spear.ai/user/5\n```\n\n```console\n# ✗ Bad\ncurl https://api.spear.ai/users\ncurl https://api.spear.ai/users/5\n```\n\n```sql\n-- ✓ Good\nSELECT * FROM person\nSELECT * FROM person_address\n```\n\n```sql\n-- ✗ Bad\nSELECT * FROM people\nSELECT * FROM people_addresses\n```\n\n## Data formats\n\n### Angle\n\n#### ¶ Eq.nB: DO prefer degrees to radians\n\nDegrees are easier to reason about. Moreover, Radians serialize poorly because they are based on the irrational number `π`.\n\n```ts\n// ✓ Good\nconst turnLeft = (angle: number) =\u003e (360 + (angle - 90)) % 360;\nturnLeft(0); // ⇒ 270\n```\n\n```ts\n// ✗ Bad\nconst turnLeft = (angle: number) =\u003e (2 * Math.PI + (angle - 0.5 * Math.PI)) % (2 * Math.PI);\nturnLeft(0); // ⇒ 4.71238898038469\n```\n\n_Exception: A series of Math functions that require radians._\n\n#### ¶ Eq.z5: DO display degrees with symbol (°)\n\n```py\n# ✓ Good\nprint(f\"heading {heading}°\")\nprint(f\"{latitude}°, {longitude}°\")\n```\n\n```py\n# ✗ Bad\nprint(f\"heading {heading}\")\nprint(f\"{latitude}, {longitude}\")\n```\n\n### Color\n\n#### ¶ Ts.Ef: DO prefer hex color codes\n\n```ts\n// ✓ Good\nconst successHexColorCode = \"#297c3b\";\nconst failureHexColorCode = \"#ca3214\";\n```\n\n```ts\n// ✗ Bad\nconst successRgbColorCode = \"rgb(41, 124, 59)\";\nconst failureRgbColorCode = \"rgb(202, 50, 20)\";\n```\n\n_Exception: A library requires another format._\n_Exception: Manipulation is easier in another format. (e.g. HSL)_\n\n#### ¶ Ts.8M: DO use longhand hex color codes\n\n```ts\n// ✓ Good\nconst color = \"#ff0000\";\nconst colorAlpha = \"#ff0000ff\";\n```\n\n```ts\n// ✗ Bad\nconst color = \"#f00\";\nconst colorAlpha = \"#f00f\";\n```\n\n#### ¶ Ts.G0: DO use lowercase hex color codes\n\n```ts\n// ✓ Good\nconst backgroundColor = \"#edf6ff\";\nconst foregroundColor = \"#006adc\";\n```\n\n```ts\n// ✗ Bad\nconst backgroundColor = \"#EDF6FF\";\nconst foregroundColor = \"#006ADC\";\n```\n\n#### ¶ Ts.2g: DO make color format explict in non-HTML contexts\n\n```graphql\n# ✓ Good\ntype ClassLabel {\n  id: ID!\n  hexColorCode: HexColorCode\n}\n```\n\n```graphql\n# ✗ Bad\ntype ClassLabel {\n  id: ID!\n  color: HexColorCode\n}\n```\n\n```sql\n-- ✓ Good\nSELECT id, hex_color_code FROM class_label\n```\n\n```sql\n-- ✗ Bad\nSELECT id, color FROM class_label\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspear-ai%2Fcitizen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspear-ai%2Fcitizen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspear-ai%2Fcitizen/lists"}