{"record":{"id":"5e327a9dc4011a76","repo":"swc-project/swc","slug":"unexpected-characters-in-id-selector","errorCode":null,"errorMessage":"Unexpected characters in ID selector","messagePattern":"Unexpected characters in ID selector","errorType":"validation","errorClass":"swc_css_parser::error::Error","httpStatus":null,"severity":"error","filePath":"crates/swc_css_parser/src/parser/selectors/mod.rs","lineNumber":631,"sourceCode":"                    ErrorKind::Expected(\"id, class, attribute or pseudo-class selector\"),\n                ));\n            }\n        }\n    }\n}\n\nimpl<I> Parse<IdSelector> for Parser<I>\nwhere\n    I: ParserInput,\n{\n    fn parse(&mut self) -> PResult<IdSelector> {\n        let span = self.input.cur_span();\n        let text = match bump!(self) {\n            Token::Hash {\n                is_id, value, raw, ..\n            } => {\n                if !is_id {\n                    return Err(Error::new(\n                        span,\n                        ErrorKind::Unexpected(\"characters in ID selector\"),\n                    ));\n                }\n\n                Ident {\n                    span,\n                    value,\n                    raw: Some(raw),\n                }\n            }\n            _ => {\n                unreachable!()\n            }\n        };\n\n        Ok(IdSelector {\n            span: span!(self, span.lo),","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_css_parser/src/parser/selectors/mod.rs#L613-L649","documentation":"CSS tokenizes '#...' as a Hash token with is_id=true only when the remainder would form a valid identifier; otherwise (e.g. '#123') is_id=false and it is not usable as an ID selector. The parser consumed a Hash token whose is_id flag is false, meaning the text after '#' contains characters that cannot start an identifier (digits, or unescaped specials), so it rejects it as an ID selector.","triggerScenarios":"'#123abc {}', '#1st {}', or any '#'-selector whose name starts with a digit or otherwise is not a valid identifier without escaping.","commonSituations":"IDs generated from database numeric keys ('#4021') rendered into CSS by templates; anchors like '#2023-report' referenced as style selectors; authors assuming HTML's looser id rules apply to CSS ID-selector syntax.","solutions":["Escape the first digit with its hex escape and a space: '#\\31 23abc' (escape for '1' plus separating space)","Prefer a class selector '.item-123' for machine-generated names","When generating selectors from IDs, run them through an identifier-escaping helper (CSS.escape in JS)"],"exampleFix":"/* before */\n#1st { color: red; }\n/* after */\n#\\31 st { color: red; }  /* or rename the id to a letter-first value */","handlingStrategy":"type-guard","validationCode":"// IDs usable as selectors must be ident-shaped after '#'\nfunction isCssIdent(s) { return /^--?[A-Za-z_][\\w-]*$/.test(s) || /^\\[0-9a-fA-F]{1,6} /.test(s); }","typeGuard":"// JS (browser/helpers)\nfunction idIsSelectable(id) { return /^[A-Za-z_-][\\w-]*$/.test(id); }\n// escape otherwise: CSS.escape(id)","tryCatchPattern":"// JS\nconst sel = idIsSelectable(id) ? `#${id}` : `#${CSS.escape ? CSS.escape(id) : escapeIdent(id)}`;","preventionTips":["Run CSS.escape over any interpolated id before building a selector","Prefer letter-first ids in generated markup","For numeric ids, target them with [id=\"123\"] attribute syntax instead"],"tags":["css","selector","id-selector","escaping"],"backgroundTag":"css-selector-syntax-error","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}