[{"data":1,"prerenderedAt":555},["ShallowReactive",2],{"page-ru-\u002Fposts\u002Fstop-using-html-for-your-resume":3},{"id":4,"title":5,"body":6,"date":542,"description":543,"duration":544,"extension":218,"meta":545,"navigation":546,"path":547,"robots":548,"seo":549,"sitemap":550,"stem":551,"tags":552,"toc":546,"__hash__":554},"content_ru\u002Fposts\u002Fstop-using-html-for-your-resume.md","Не используйте HTML для резюме",{"type":7,"value":8,"toc":527},"minimark",[9,17,24,29,39,110,114,117,122,125,140,143,147,159,193,196,200,203,236,240,243,246,250,256,271,282,291,324,328,332,342,356,359,368,380,407,410,413,423,426,430,433,438,465,470,488,496,500,514,523],[10,11,13],"h1",{"id":12},"",[14,15],"binding",{"value":16},"title",[18,19],"posts-date-duration",{":duration":20,":date":21,"className":22},"duration","date",[23],"-mt-6",[25,26,28],"h2",{"id":27},"дисклеймер","Дисклеймер",[30,31,32,33,38],"p",{},"Эта статья — логическое продолжение ",[34,35,37],"a",{"href":36},"\u002Fposts\u002Fstop-using-word-for-your-resume","Не используйте Word для резюме",". Настоятельно рекомендую сначала прочитать ее для полного контекста, но я все же кратко напомню основные тезисы:",[40,41,42,55,95,101],"ol",{},[43,44,45,46,50,51,54],"li",{},"Резюме должно иметь ",[47,48,49],"strong",{},"единый локальный"," источник истины в легко редактируемом ",[47,52,53],{},"сыром"," формате.",[43,56,57,58,61,62,65,66,69,70,73,74,65,77,65,80,69,83,86,87,90,91,94],{},"Этот сырой формат должен ",[47,59,60],{},"конвертироваться в PDF",", быть ",[47,63,64],{},"дружелюбным к LLM и ATS",", ",[47,67,68],{},"версионируемым"," и ",[47,71,72],{},"портативным",", с ",[47,75,76],{},"точным контролем верстки",[47,78,79],{},"богатым и гибким оформлением",[47,81,82],{},"удобством написания контента",[47,84,85],{},"разделением контента и стиля",". Мы предполагаем, что пользователь — ",[47,88,89],{},"разработчик"," и\u002Fили ",[47,92,93],{},"может или хочет освоить"," этот формат.",[43,96,97,100],{},[47,98,99],{},"HTML"," подходит на эту роль, но не имел нужного инструментария.",[43,102,103,109],{},[34,104,108],{"href":105,"rel":106},"https:\u002F\u002Fcv-html-dev.netlify.app",[107],"nofollow","CV.html"," был создан, чтобы закрыть этот пробел.",[25,111,113],{"id":112},"проблемы","Проблемы",[30,115,116],{},"С тех пор я много работал со своим инструментом, экспериментируя со стилями, контентом, визуальным дизайном и прочим. На бумаге все звучит отлично, но на практике проявились неожиданные проблемы.",[118,119,121],"h3",{"id":120},"повторения","Повторения",[30,123,124],{},"Структура резюме по своей природе повторяема — записи о работе, пункты списков, навыки — но чистый HTML не дает способа это выразить. Например, достижения внутри записи об опыте работы или сами записи об опыте.",[30,126,127,128,133,134,139],{},"Технически это двухуровневый список, и в повседневной работе мы бы использовали циклы и компонентную абстракцию, но чистый HTML не предоставляет таких инструментов. Можно вынести часть разметки в ",[34,129,132],{"href":130,"rel":131},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWeb_components\u002FUsing_custom_elements",[107],"custom element",", но все равно придется повторять его много раз в коде, раздувая файл и натыкаясь на все, о чем говорит принцип ",[34,135,138],{"href":136,"rel":137},"https:\u002F\u002Fru.wikipedia.org\u002Fwiki\u002FDon%E2%80%99t_repeat_yourself",[107],"DRY",".",[30,141,142],{},"К тому же кто захочет писать веб компоненты для своего резюме?",[118,144,146],{"id":145},"изображения","Изображения",[30,148,149,150,154,155,158],{},"В некоторых странах принято добавлять фото в резюме, и самый портативный способ встроить его в HTML — конвертировать в base64-строку. Если вы хоть раз видели закодированное изображение, знаете, что оно может быть очень большим. В текущей реализации пользователю приходится вставлять его в тег ",[151,152,153],"code",{},"img"," как атрибут ",[151,156,157],{},"src",":",[160,161,165],"pre",{"className":162,"code":163,"language":164,"meta":12,"style":12},"language-html shiki shiki-themes github-light github-dark","\u003Cimg src=\"data:image\u002F[format];base64,[your_base64_string]\" \u002F>\n","html",[151,166,167],{"__ignoreMap":12},[168,169,172,176,179,183,186,190],"span",{"class":170,"line":171},"line",1,[168,173,175],{"class":174},"sVt8B","\u003C",[168,177,153],{"class":178},"s9eBZ",[168,180,182],{"class":181},"sScJk"," src",[168,184,185],{"class":174},"=",[168,187,189],{"class":188},"sZZnC","\"data:image\u002F[format];base64,[your_base64_string]\"",[168,191,192],{"class":174}," \u002F>\n",[30,194,195],{},"Это идеально сочетается с предыдущей проблемой, делая HTML еще более раздутым. А теперь представьте, что одно и то же изображение нужно отрендерить несколько раз (как в случае со списком опыта работы). Одна мысль об этом — и уже болит голова.",[118,197,199],{"id":198},"мультиязычность","Мультиязычность",[30,201,202],{},"Один из моих сценариев — создание двуязычных резюме с одинаковым внешним видом. В процессе оказлось что это гораздо трудозатратнее из-за проблем выше плюс из-за смешения тегов, классов и самого контента. Можно вставить HTML файл в LLM и попросить перевести, но:",[40,204,205,208,211],{},[43,206,207],{},"Все равно придется проверять результат и вносить изменения.",[43,209,210],{},"Закодированные изображения съедают лишние токены.",[43,212,213,214,232,235],{},"Сравните китайскую и английскую версии одного предложения:",[160,215,219],{"className":216,"code":217,"language":218,"meta":12,"style":12},"language-md shiki shiki-themes github-light github-dark","> London is the capital of the United Kingdom.\n> 伦敦是英国的首都。\n","md",[151,220,221,226],{"__ignoreMap":12},[168,222,223],{"class":170,"line":171},[168,224,225],{"class":178},"> London is the capital of the United Kingdom.\n",[168,227,229],{"class":170,"line":228},2,[168,230,231],{"class":178},"> 伦敦是英国的首都。\n",[233,234],"br",{},"Очевидно, что китайская версия короче английской. Если просто перевести тот же HTML-шаблон, блоки фиксированного размера и разрывы страниц поплывут. Например, двухстраничное китайское резюме может превратиться в трех- или четырехстраничное английское, что уже может считаться перегруженным. Значит, придется подстраивать стили и структуру шаблона. Помните пример со списком опыта работы? Страшно!",[25,237,239],{"id":238},"решение","Решение",[30,241,242],{},"Не используйте HTML для резюме. Или нет?",[30,244,245],{},"Когда я столкнулся с этими проблемами, начал думать, как сохранить плюсы HTML и убрать минусы. И нашел очень хорошее решение.",[118,247,249],{"id":248},"концепция","Концепция",[30,251,252,253,139],{},"Нужно понять настоящую суть проблемы. Вернемся к нашим требованиям — одно из них было ",[47,254,255],{},"разделение контента и стиля",[30,257,258,259,262,263,266,267,270],{},"CV.html имеет три вкладки: ",[47,260,261],{},"Template",", где пользователь пишет HTML; ",[47,264,265],{},"Styles",", где пишет CSS; и ",[47,268,269],{},"Head"," для дополнительных внешних импортов (Tailwind CSS, шрифты и т.д.). Формально разделение контента и стиля через вкладки Template и Styles существует, но на практике все было наоборот.",[30,272,273,274,139],{},"Потому что этот пункт нужно сформулировать чуть иначе: ",[47,275,276,277,281],{},"разделение контента, ",[278,279,280],"em",{},"шаблона"," и стиля",[30,283,284,285,69,288,290],{},"Сначала нужно разложить HTML на части ",[47,286,287],{},"контента",[47,289,280],{},". Определим их:",[292,293,294,304],"ul",{},[43,295,296,299,300,303],{},[47,297,298],{},"Контент"," — вся ",[278,301,302],{},"информация"," в резюме: текст, изображения и любые другие метаданные, нужные шаблону.",[43,305,306,309,310,313,314,317,318,69,320,323],{},[47,307,308],{},"Шаблон"," — ",[278,311,312],{},"структура"," резюме. Его также можно описать как ",[278,315,316],{},"компиляцию"," ",[47,319,287],{},[47,321,322],{},"стилей"," по правилам пользователя.",[118,325,327],{"id":326},"реализация","Реализация",[329,330,308],"h4",{"id":331},"шаблон",[30,333,334,335,337,338,341],{},"Главная техническая проблема — раздел ",[47,336,261],{},". Озвученные проблемы HTML не новы — их уже решили frontend-",[278,339,340],{},"фреймворки",". Но создавать React-приложение для резюме — оверинжиниринг: оно будет тяжелым, пользователю не понадобится большинство возможностей, усложнится экспорт в PDF, а экспорт сырого HTML станет недоступен. Для комфортной работы нужны только следующие возможности фреймворка:",[40,343,344,347,353],{},[43,345,346],{},"Циклы и условный рендеринг, чтобы убрать повторения.",[43,348,349,350,139],{},"Привязка к динамическим переменным, передаваемым отдельным ",[47,351,352],{},"контентом",[43,354,355],{},"Статический HTML на выходе.",[30,357,358],{},"Это не самый частый сценарий, но вполне валидный — и идеальный инструмент уже существует.",[30,360,361,362,367],{},"Встречайте, ",[34,363,366],{"href":364,"rel":365},"https:\u002F\u002Fhandlebarsjs.com",[107],"Handlebars","!",[30,369,370,371,373,374,379],{},"Коротко, ",[47,372,366],{}," — простой язык шаблонов, который принимает шаблон и входной объект и генерирует HTML или другие текстовые форматы. Подробнее о возможностях — в ",[34,375,378],{"href":376,"rel":377},"https:\u002F\u002Fhandlebarsjs.com\u002Fguide",[107],"документации",", но я остановлюсь только на важном для нас:",[40,381,382,396,404],{},[43,383,384,385,69,390,395],{},"Есть ",[34,386,389],{"href":387,"rel":388},"https:\u002F\u002Fhandlebarsjs.com\u002Fguide\u002Fbuiltin-helpers.html#each",[107],"циклы",[34,391,394],{"href":392,"rel":393},"https:\u002F\u002Fhandlebarsjs.com\u002Fguide\u002Fbuiltin-helpers.html#if",[107],"условный рендеринг"," из коробки.",[43,397,384,398,403],{},[34,399,402],{"href":400,"rel":401},"https:\u002F\u002Fhandlebarsjs.com\u002Fguide\u002Fexpressions.html#basic-usage",[107],"привязки"," к переменным-объектам.",[43,405,406],{},"Можно скомпилировать в HTML в рантайме.",[30,408,409],{},"К тому же синтаксис очень похож на современные frontend-фреймворки, так что бонусом моим коллегам-фронтендерам его будет легко освоить.",[329,411,298],{"id":412},"контент",[30,414,415,416,418,419,422],{},"Простая часть — извлечь ",[47,417,412],{}," из HTML в JS-объект. Можно создать JSON или YAML файл, где пользователь определяет свой ",[151,420,421],{},"cv"," — все текстовые данные, атрибуты вроде base64-строки изображения и т.д. — и затем распарсить его.",[30,424,425],{},"Потом передаем этот объект в Handlebars и даем пользователю обращаться к нему в шаблоне. Я выбрал YAML, потому что он более читаем для человека, чем JSON.",[118,427,429],{"id":428},"результат","Результат",[30,431,432],{},"Я уже реализовал это решение, протестировал — работает отлично и дает огромную гибкость. Вот как это выглядит на практике:",[30,434,435,436,139],{},"Хотите двуязычное резюме? Или тот же шаблон для другого человека? Достаточно заменить ",[47,437,412],{},[439,440,446,447,446,454,446,459,464],"div",{"className":441},[442,443,444,445],"flex","gap-2","md:flex-row","flex-col","\n    ",[448,449],"posts-image",{"src":450,"alt":451,"caption":451,"className":452},"\u002Fimages\u002Fcvhtml-original.png","Оригинальное резюме CV.html",[453],"h-70",[448,455],{"src":456,"alt":457,"caption":457,"className":458},"\u002Fimages\u002Fcvhtml-russian.png","То же резюме, переведенное на русский",[453],[448,460],{"src":461,"alt":462,"caption":462,"className":463},"\u002Fimages\u002Fcvhtml-johndoe.png","Тот же шаблон для другого пользователя",[453],"\n  ",[30,466,467,468,139],{},"Или хотите другой дизайн? Без проблем — просто замените ",[47,469,331],{},[439,471,446,473,446,478,446,483,464],{"className":472},[442,443,444,445],[448,474],{"src":475,"alt":476,"caption":476,"className":477},"\u002Fimages\u002Fcvhtml-pinkycute.png","Шаблон Pinky-cute",[453],[448,479],{"src":480,"alt":481,"caption":481,"className":482},"\u002Fimages\u002Fcvhtml-newspaper.png","Шаблон Newspaper",[453],[448,484],{"src":485,"alt":486,"caption":486,"className":487},"\u002Fimages\u002Fcvhtml-terminal.png","Шаблон Terminal",[453],[30,489,490,491,139],{},"Вариантов кастомизации так много, что я до сих пор нахожу новые идеи использования и дизайны. При желании, вы тоже можете поэксперементировать в ",[492,493],"cv-html-badge",{"className":494},[495],"ml-1",[25,497,499],{"id":498},"заключение","Заключение",[30,501,502,503,506,507,69,510,513],{},"HTML все еще лучший ",[278,504,505],{},"сырой"," формат? Технически нет — мы заменили его двумя независимыми файлами ",[151,508,509],{},"yaml",[151,511,512],{},"hbs"," (Handlebars). Но он по-прежнему служит промежуточным форматом между исходными файлами и PDF, и вы по-прежнему можете использовать плюсы HTML — например, отрендерить результат на сайте.",[30,515,516,517,519,520,522],{},"Поэтому я бы все же сказал: относитесь к HTML как к выходному формату, а резюме пишите в абстракциях ",[47,518,287],{}," \u002F ",[47,521,280],{},". Чистый HTML не дает такого разделения, но в экосистеме уже существуют все нужные инструменты, чтобы этого достичь.",[524,525,526],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":12,"searchDepth":228,"depth":228,"links":528},[529,530,536,541],{"id":27,"depth":228,"text":28},{"id":112,"depth":228,"text":113,"children":531},[532,534,535],{"id":120,"depth":533,"text":121},3,{"id":145,"depth":533,"text":146},{"id":198,"depth":533,"text":199},{"id":238,"depth":228,"text":239,"children":537},[538,539,540],{"id":248,"depth":533,"text":249},{"id":326,"depth":533,"text":327},{"id":428,"depth":533,"text":429},{"id":498,"depth":228,"text":499},"2026-06-13 00:00:00 UTC","Продолжение темы резюме и крупные улучшения в CV.html","8 мин",{},true,"\u002Fposts\u002Fstop-using-html-for-your-resume",null,{"title":5,"description":543},{"loc":547},"posts\u002Fstop-using-html-for-your-resume",[553],"dev","sD49shdqr640DIBITIiJBDqqdhL7iwKLvi3Ddb-d2ns",1784559756048]