diff --git a/LAST_VERSION b/LAST_VERSION index 66c4c22..0e8c70a 100644 --- a/LAST_VERSION +++ b/LAST_VERSION @@ -1 +1 @@ -1.0.9 +1.0.9-dev1 diff --git a/docs/nav/development/component-showcase.md b/docs/nav/development/component-showcase.md new file mode 100644 index 0000000..9ca564a --- /dev/null +++ b/docs/nav/development/component-showcase.md @@ -0,0 +1,126 @@ +# Component Showcase + +This page exercises every themed component using real content about the +[Dracula theme](https://github.com/dracula) project, so visual regressions +are easy to spot during development. + +## What is Dracula? + +Dracula is a dark theme for code editors, terminals, and hundreds of other +applications, created by [Zeno Rocha](https://github.com/zenorocha). It +started as a single Sublime Text theme and has since grown into one of the +most widely ported color schemes in open source. + +### Design goals + +# A single color scheme, everywhere +## Consistency across every tool you use +### From the editor to the terminal +#### From the terminal to the browser +##### From the browser to your IDE plugins +###### Down to the smallest utility script + +## The philosophy + +Dracula follows a few simple rules, explained in **bold**, *italics*, +***bold italics***, and even ~~occasionally~~ always with a bit of +`inline code` for terms like `background`, `foreground`, and `selection`. + +Read the [official spec](https://draculatheme.com/contribute) before +porting the theme to a new [application](https://draculatheme.com). + +## Why people port it + +- It looks good on every screen, day or night +- It is consistent across dozens of tools + - Editors: VS Code, Vim, Sublime Text + - Terminals: iTerm2, Alacritty, Windows Terminal + - Browsers, IDEs, and even Slack + +1. Pick a base application to port +2. Follow the official color specification +3. Submit the port for review + 1. Maintainers check contrast and accuracy + 2. Once approved, it ships on draculatheme.com + +## In the maintainers' words + +> Dracula is not just a theme, it's a community. Every port follows the +> same spec so that switching tools never means switching colors. + +## The official palette + +```python +palette = { + "background": "#282A36", + "current_line": "#44475A", + "foreground": "#F8F8F2", + "comment": "#6272A4", + "cyan": "#8BE9FD", + "green": "#50FA7B", + "orange": "#FFB86C", + "pink": "#FF79C6", + "purple": "#BD93F9", + "red": "#FF5555", + "yellow": "#F1FA8C", +} +``` + +## Palette reference + +| Name | Hex | Usage | +| ------------ | --------- | ----------------------- | +| Background | `#282A36` | Editor background | +| Current Line | `#44475A` | Selection / active line | +| Foreground | `#F8F8F2` | Default text | +| Comment | `#6272A4` | Comments, muted text | +| Purple | `#BD93F9` | Keywords | +| Pink | `#FF79C6` | Operators | +| Green | `#50FA7B` | Strings | + +## Notes for contributors + +!!! note + Every port must follow the [official spec](https://draculatheme.com/contribute) exactly — no custom colors. + +!!! abstract + tl;dr: fork the [contributing guide repo](https://github.com/dracula/contributing-guide), copy the closest existing port, swap the palette. + +!!! info + Dracula has 400+ official ports, from editors to hardware keyboards. + +!!! tip + Test your port against real code with syntax highlighting before submitting — flat colors alone can hide contrast issues. + +!!! success + Once merged, your port is listed on [draculatheme.com](https://draculatheme.com) and this `mkdocs` theme is one of them. + +!!! question + Not sure which repo to fork? Check the [ports list](https://draculatheme.com) for a project similar to yours. + +!!! warning + Don't rename the official color variables (`--purple`, `--cyan`, etc.) — themes and tooling depend on those names staying stable. + +!!! failure + A port that changes background/foreground contrast ratios below WCAG AA will be rejected in review. + +!!! danger + Never hardcode hex values in component CSS — always reference the palette variables, or the port breaks when the palette updates. + +!!! bug + Found a color mismatch against the spec? Open an issue on the [ports repo](https://github.com/dracula). + +!!! example + This very page is an example: it's built with `mkdocs-dracula-theme`, itself a Dracula port for MkDocs sites. + +!!! quote + "The most famous theme ever created and available everywhere." — [draculatheme.com](https://draculatheme.com) + +!!! warning "Before you open a PR" + Read `CONTRIBUTING.md` in the target repo — most ports have screenshot and structure requirements beyond just the colors. + +## Footer + +This page's footer is controlled by `theme.show_footer` in `mkdocs.yml`, +linking back to the [dracula/mkdocs](https://github.com/dracula/mkdocs) +repository unless disabled. diff --git a/mkdocs.yml b/mkdocs.yml index 6a7bffa..493147e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -13,8 +13,13 @@ nav: - nav/settings/show-footer.md - Development: - nav/development/development-guide.md + - nav/development/component-showcase.md - nav/development/release-notes.md +markdown_extensions: + - admonition + - tables + theme: name: dracula logo: assets/img/dracula.png diff --git a/mkdocs_dracula_theme/__init__.py b/mkdocs_dracula_theme/__init__.py index 800fa1c..8c281d5 100644 --- a/mkdocs_dracula_theme/__init__.py +++ b/mkdocs_dracula_theme/__init__.py @@ -1,4 +1,4 @@ -__version__ = "1.0.9" +__version__ = "1.0.9-dev1" __author__ = "Fernando Celmer " __copyright__ = """MIT License diff --git a/mkdocs_dracula_theme/assets/css/mkdocs.min.css b/mkdocs_dracula_theme/assets/css/mkdocs.min.css index 56dd4a4..2130b24 100644 --- a/mkdocs_dracula_theme/assets/css/mkdocs.min.css +++ b/mkdocs_dracula_theme/assets/css/mkdocs.min.css @@ -1 +1,2 @@ -code,pre{background-color:var(--black)}footer,table{width:100%}.content,.sidebar ul.components{padding:0}.active,.bold,.sidebar h1{font-weight:700}.normal,a{text-decoration:none}a{color:var(--white)}a:hover{color:var(--pink)}footer{position:fixed;bottom:0}.footer-text{font-size:14px!important;margin-top:0!important;margin-bottom:0!important}pre{margin:1rem 0;padding:1rem 1rem 10px;font-size:.775em}article{padding-left:5%;padding-right:5%}table,td,th{border-color:var(--greySecondary)!important;padding-right:15px!important;border-width:1px;border-style:solid;padding:1%}h1,h2,h3,h4{margin-top:1.8rem!important}img,svg{max-width:100%;margin-left:auto;margin-right:auto}.logo{display:block;margin-bottom:10%;width:150px!important}.sidebar{min-width:270px;max-width:270px;-webkit-transition:.3s;-o-transition:.3s;transition:.3s;position:sticky}.sidebar.active{margin-left:-270px}.sidebar h1{margin-bottom:20px;font-size:30px}.sidebar h1 .logo span{font-size:14px;display:block}.sidebar ul li{font-size:16px}.sidebar ul li>ul{margin-left:10px}.sidebar ul li>ul li{font-size:14px}.sidebar ul li a{padding:10px 0;display:block;border-bottom:1px solid rgba(255,255,255,.1)}.sidebar .custom-menu{display:inline-block;position:absolute;top:81px;right:0;margin-right:-41px;-webkit-transition:.3s;-o-transition:.3s;transition:.3s}.sidebar .custom-menu .btn{width:60px;height:60px;border-radius:50%;position:relative}.sidebar .custom-menu .btn i{margin-right:00px;font-size:18px}.sidebar .custom-menu .btn.btn-primary{background:0 0;border-color:transparent}.sidebar .custom-menu .btn.btn-primary:after{z-index:-1;position:absolute;top:0;left:0;right:0;bottom:0;content:'';-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);background:linear-gradient(var(--gradientDegree),var(--purple) 0,var(--cyan) 100%);border-radius:10px}.sidebar .custom-menu .btn.btn-primary:focus,.sidebar .custom-menu .btn.btn-primary:hover{background:0 0!important;border-color:transparent!important}@media (prefers-reduced-motion:reduce){.sidebar .custom-menu{-webkit-transition:none;-o-transition:none;transition:none}}@media (max-width:991.98px){.sidebarCollapse span{display:none}.sidebar{margin-left:-270px}.sidebar.active{margin-left:0}}.content{width:100%;min-height:100vh;-webkit-transition:.3s;-o-transition:.3s;transition:.3s}.content-search,.section-content{padding-top:7px!important}.dot-ul{list-style:none;display:flex;margin:10px 0 0;padding-left:23%}.dot-li{width:12px;height:12px;margin:3px;border-radius:100%}.md-source-repo-icon{display:inline-block;vertical-align:middle;margin-right:10px;margin-left:10px;font-size:xx-large}.md-source-repo-text{display:flex;font-size:14px;text-align:left;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source-repo-url{display:inline-block;max-width:calc(100% - 1.2rem);white-space:nowrap;width:15em;overflow:hidden;text-overflow:ellipsis;font-size:16px;text-align:left;vertical-align:middle}.flex-column{flex-direction:row-reverse!important}.divider{min-width:10px!important;flex-shrink:0;width:1.5rem}.btn-preview,.btn-preview:disabled{width:170px}.btn-preview:hover{border-color:var(--green)}.row-preview{padding:5px}.drac-bg-grey-ternary{--accentColor:var(--greyTernary);background-color:var(--greyTernary)}.drac-text-grey-ternary{color:var(--greyTernary)}@media all and (min-width:992px){.dropdown-menu li{position:relative}.nav-item .submenu{display:none;position:absolute;left:100%;top:-7px}.nav-item .submenu-left{right:100%;left:auto}.dropdown-menu>li:hover{background-color:#f1f1f1}.dropdown-menu>li:hover>.submenu{display:block}}@media (max-width:991px){.dropdown-menu .dropdown-menu{margin-left:.7rem;margin-right:.7rem;margin-bottom:.5rem}}.normal{font-weight:400;font-style:normal}.italic{font-style:italic}.scratched{text-decoration:line-through}.underline{text-decoration:underline}.overline{text-decoration:overline}@media only screen and (max-width:780px){a,code,h1,h2,h3,h4,h5,h6,li,p,small{margin-top:1rem!important;margin-bottom:1rem!important}.sidebar ul li a{padding:1%;font-size:15px!important;margin-top:1%!important;margin-bottom:8%!important}.site-name{font-size:14px!important}.title,h1,h2,h3,h4,h5,h6{font-size:18px!important}.container{padding-left:5%!important;padding-right:5%!important}main{margin-left:0!important;margin-right:0!important;line-height:1.8!important}a,code,li,p,small{font-size:12px!important}article{padding-left:10%;padding-right:10%}.section-content{padding-top:7px!important;padding-bottom:20%!important}.footer-text{font-size:12px!important;margin-top:0!important;margin-bottom:0!important}}.search-box{margin-top:2%;margin-bottom:1%}.btn-toggle{color:var(--white)!important;background-color:transparent;display:inline-flex!important;text-decoration:none;transition:.16s ease-in-out}.btn-toggle:focus,.btn-toggle:hover{color:var(--purple)!important;background:var(--black)!important}.btn-toggle::after{width:1.25em;line-height:0;content:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");transition:transform .35s;transform-origin:.5em 50%;margin-left:10px}[data-bs-theme=dark] .btn-toggle::after{content:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%28248,248,242,1%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e")}.btn-toggle[aria-expanded=true]{color:rgba(var(--bs-emphasis-color-rgb),.85)}.btn-toggle[aria-expanded=true]::after{transform:rotate(90deg)}.drac-anchor-secondary{margin-left:10%}.drac-box-ternary{margin-top:3%;margin-left:10%}.drac-input-search{margin-top:2px;width:170px} \ No newline at end of file +code,pre{background-color:var(--black)}footer,table{width:100%}.content,.sidebar ul.components{padding:0}.active,.bold,.sidebar h1{font-weight:700}.normal,a{text-decoration:none}a{color:var(--white)}a:hover{color:var(--pink)}footer{position:fixed;bottom:0}.footer-text{font-size:14px!important;margin-top:0!important;margin-bottom:0!important}pre{margin:1rem 0;padding:1rem 1rem 10px;font-size:.775em}article{padding-left:5%;padding-right:5%}table,td,th{border-color:var(--greySecondary)!important;padding-right:15px!important;border-width:1px;border-style:solid;padding:1%}h1,h2,h3,h4{margin-top:1.8rem!important}img,svg{max-width:100%;margin-left:auto;margin-right:auto}.logo{display:block;margin-bottom:10%;width:150px!important}.sidebar{min-width:270px;max-width:270px;-webkit-transition:.3s;-o-transition:.3s;transition:.3s;position:sticky}.sidebar.active{margin-left:-270px}.sidebar h1{margin-bottom:20px;font-size:30px}.sidebar h1 .logo span{font-size:14px;display:block}.sidebar ul li{font-size:16px}.sidebar ul li>ul{margin-left:10px}.sidebar ul li>ul li{font-size:14px}.sidebar ul li a{padding:10px 0;display:block;border-bottom:1px solid rgba(255,255,255,.1)}.sidebar .custom-menu{display:inline-block;position:absolute;top:81px;right:0;margin-right:-41px;-webkit-transition:.3s;-o-transition:.3s;transition:.3s}.sidebar .custom-menu .btn{width:60px;height:60px;border-radius:50%;position:relative}.sidebar .custom-menu .btn i{margin-right:00px;font-size:18px}.sidebar .custom-menu .btn.btn-primary{background:0 0;border-color:transparent}.sidebar .custom-menu .btn.btn-primary:after{z-index:-1;position:absolute;top:0;left:0;right:0;bottom:0;content:'';-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);background:linear-gradient(var(--gradientDegree),var(--purple) 0,var(--cyan) 100%);border-radius:10px}.sidebar .custom-menu .btn.btn-primary:focus,.sidebar .custom-menu .btn.btn-primary:hover{background:0 0!important;border-color:transparent!important}@media (prefers-reduced-motion:reduce){.sidebar .custom-menu{-webkit-transition:none;-o-transition:none;transition:none}}@media (max-width:991.98px){.sidebarCollapse span{display:none}.sidebar{margin-left:-270px}.sidebar.active{margin-left:0}}.content{width:100%;min-height:100vh;-webkit-transition:.3s;-o-transition:.3s;transition:.3s}.content-search,.section-content{padding-top:7px!important}.dot-ul{list-style:none;display:flex;margin:10px 0 0;padding-left:23%}.dot-li{width:12px;height:12px;margin:3px;border-radius:100%}.md-source-repo-icon{display:inline-block;vertical-align:middle;margin-right:10px;margin-left:10px;font-size:xx-large}.md-source-repo-text{display:flex;font-size:14px;text-align:left;gap:.4rem;list-style-type:none;margin:.1rem 0 0;opacity:.75;overflow:hidden;padding:0;width:100%}.md-source-repo-url{display:inline-block;max-width:calc(100% - 1.2rem);white-space:nowrap;width:15em;overflow:hidden;text-overflow:ellipsis;font-size:16px;text-align:left;vertical-align:middle}.flex-column{flex-direction:row-reverse!important}.divider{min-width:10px!important;flex-shrink:0;width:1.5rem}.btn-preview,.btn-preview:disabled{width:170px}.btn-preview:hover{border-color:var(--green)}.row-preview{padding:5px}.drac-bg-grey-ternary{--accentColor:var(--greyTernary);background-color:var(--greyTernary)}.drac-text-grey-ternary{color:var(--greyTernary)}@media all and (min-width:992px){.dropdown-menu li{position:relative}.nav-item .submenu{display:none;position:absolute;left:100%;top:-7px}.nav-item .submenu-left{right:100%;left:auto}.dropdown-menu>li:hover{background-color:#f1f1f1}.dropdown-menu>li:hover>.submenu{display:block}}@media (max-width:991px){.dropdown-menu .dropdown-menu{margin-left:.7rem;margin-right:.7rem;margin-bottom:.5rem}}.normal{font-weight:400;font-style:normal}.italic{font-style:italic}.scratched{text-decoration:line-through}.underline{text-decoration:underline}.overline{text-decoration:overline}@media only screen and (max-width:780px){a,code,h1,h2,h3,h4,h5,h6,li,p,small{margin-top:1rem!important;margin-bottom:1rem!important}.sidebar ul li a{padding:1%;font-size:15px!important;margin-top:1%!important;margin-bottom:8%!important}.site-name{font-size:14px!important}.title,h1,h2,h3,h4,h5,h6{font-size:18px!important}.container{padding-left:5%!important;padding-right:5%!important}main{margin-left:0!important;margin-right:0!important;line-height:1.8!important}a,code,li,p,small{font-size:12px!important}article{padding-left:10%;padding-right:10%}.section-content{padding-top:7px!important;padding-bottom:20%!important}.footer-text{font-size:12px!important;margin-top:0!important;margin-bottom:0!important}}.search-box{margin-top:2%;margin-bottom:1%}.btn-toggle{color:var(--white)!important;background-color:transparent;display:inline-flex!important;text-decoration:none;transition:.16s ease-in-out}.btn-toggle:focus,.btn-toggle:hover{color:var(--purple)!important;background:var(--black)!important}.btn-toggle::after{width:1.25em;line-height:0;content:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");transition:transform .35s;transform-origin:.5em 50%;margin-left:10px}[data-bs-theme=dark] .btn-toggle::after{content:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%28248,248,242,1%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e")}.btn-toggle[aria-expanded=true]{color:rgba(var(--bs-emphasis-color-rgb),.85)}.btn-toggle[aria-expanded=true]::after{transform:rotate(90deg)}.drac-anchor-secondary{margin-left:10%}.drac-box-ternary{margin-top:3%;margin-left:10%}.drac-input-search{margin-top:2px;width:170px} +.admonition{border-left:4px solid var(--purple);border-radius:.5rem;background:var(--purple-transparent);padding:.75rem 1rem;margin:1.25rem 0;overflow:hidden}.admonition p:last-child{margin-bottom:0}.admonition-title{font-weight:700;margin:-.75rem -1rem .75rem -1rem;padding:.5rem 1rem;display:flex;align-items:center}.admonition-title::before{font-family:FontAwesome;margin-right:.5rem;content:"\f040"}.admonition.note{border-color:var(--purple);background:var(--purple-transparent)}.admonition.note .admonition-title{background:rgba(149,128,255,.15);color:var(--purple)}.admonition.note .admonition-title::before{content:"\f040"}.admonition.abstract,.admonition.summary,.admonition.tldr{border-color:var(--cyan);background:var(--cyan-transparent)}.admonition.abstract .admonition-title,.admonition.summary .admonition-title,.admonition.tldr .admonition-title{background:rgba(128,255,234,.15);color:var(--cyan)}.admonition.abstract .admonition-title::before,.admonition.summary .admonition-title::before,.admonition.tldr .admonition-title::before{content:"\f0ea"}.admonition.info{border-color:var(--cyan);background:var(--cyan-transparent)}.admonition.info .admonition-title{background:rgba(128,255,234,.15);color:var(--cyan)}.admonition.info .admonition-title::before{content:"\f05a"}.admonition.tip,.admonition.hint,.admonition.important{border-color:var(--green);background:var(--green-transparent)}.admonition.tip .admonition-title,.admonition.hint .admonition-title,.admonition.important .admonition-title{background:rgba(138,255,128,.15);color:var(--green)}.admonition.tip .admonition-title::before,.admonition.hint .admonition-title::before,.admonition.important .admonition-title::before{content:"\f0eb"}.admonition.success,.admonition.check,.admonition.done{border-color:var(--green);background:var(--green-transparent)}.admonition.success .admonition-title,.admonition.check .admonition-title,.admonition.done .admonition-title{background:rgba(138,255,128,.15);color:var(--green)}.admonition.success .admonition-title::before,.admonition.check .admonition-title::before,.admonition.done .admonition-title::before{content:"\f058"}.admonition.question,.admonition.help,.admonition.faq{border-color:var(--yellow);background:var(--yellow-transparent)}.admonition.question .admonition-title,.admonition.help .admonition-title,.admonition.faq .admonition-title{background:rgba(255,255,128,.15);color:var(--yellow)}.admonition.question .admonition-title::before,.admonition.help .admonition-title::before,.admonition.faq .admonition-title::before{content:"\f059"}.admonition.warning,.admonition.caution,.admonition.attention{border-color:var(--orange);background:var(--orange-transparent)}.admonition.warning .admonition-title,.admonition.caution .admonition-title,.admonition.attention .admonition-title{background:rgba(255,202,128,.15);color:var(--orange)}.admonition.warning .admonition-title::before,.admonition.caution .admonition-title::before,.admonition.attention .admonition-title::before{content:"\f071"}.admonition.failure,.admonition.fail,.admonition.missing{border-color:var(--pink);background:var(--pink-transparent)}.admonition.failure .admonition-title,.admonition.fail .admonition-title,.admonition.missing .admonition-title{background:rgba(255,128,191,.15);color:var(--pink)}.admonition.failure .admonition-title::before,.admonition.fail .admonition-title::before,.admonition.missing .admonition-title::before{content:"\f057"}.admonition.danger,.admonition.error{border-color:var(--red);background:var(--red-transparent)}.admonition.danger .admonition-title,.admonition.error .admonition-title{background:rgba(255,149,128,.15);color:var(--red)}.admonition.danger .admonition-title::before,.admonition.error .admonition-title::before{content:"\f0e7"}.admonition.bug{border-color:var(--red);background:var(--red-transparent)}.admonition.bug .admonition-title{background:rgba(255,149,128,.15);color:var(--red)}.admonition.bug .admonition-title::before{content:"\f188"}.admonition.example{border-color:var(--purple);background:var(--purple-transparent)}.admonition.example .admonition-title{background:rgba(149,128,255,.15);color:var(--purple)}.admonition.example .admonition-title::before{content:"\f03a"}.admonition.quote,.admonition.cite{border-color:var(--grey);background:rgba(65,69,88,.2)}.admonition.quote .admonition-title,.admonition.cite .admonition-title{background:rgba(65,69,88,.3);color:var(--white)}.admonition.quote .admonition-title::before,.admonition.cite .admonition-title::before{content:"\f10d"} diff --git a/template/assets/css/mkdocs.css b/template/assets/css/mkdocs.css index 782342c..38e047b 100644 --- a/template/assets/css/mkdocs.css +++ b/template/assets/css/mkdocs.css @@ -476,4 +476,95 @@ img, svg { .drac-input-search { margin-top: 2px; width: 170px; -} \ No newline at end of file +} + +/* ============================================================= + Admonitions + ============================================================= */ + +.admonition { + border-left: 4px solid var(--purple); + border-radius: 0.5rem; + background: var(--purple-transparent); + padding: 0.75rem 1rem; + margin: 1.25rem 0; + overflow: hidden; +} + +.admonition p:last-child { + margin-bottom: 0; +} + +.admonition-title { + font-weight: 700; + margin: -0.75rem -1rem 0.75rem -1rem; + padding: 0.5rem 1rem; + display: flex; + align-items: center; +} + +.admonition-title::before { + font-family: FontAwesome; + margin-right: 0.5rem; + content: "\f040"; +} + +/* note */ +.admonition.note { border-color: var(--purple); background: var(--purple-transparent); } +.admonition.note .admonition-title { background: rgba(149,128,255,.15); color: var(--purple); } +.admonition.note .admonition-title::before { content: "\f040"; } + +/* abstract / summary / tldr */ +.admonition.abstract,.admonition.summary,.admonition.tldr { border-color: var(--cyan); background: var(--cyan-transparent); } +.admonition.abstract .admonition-title,.admonition.summary .admonition-title,.admonition.tldr .admonition-title { background: rgba(128,255,234,.15); color: var(--cyan); } +.admonition.abstract .admonition-title::before,.admonition.summary .admonition-title::before,.admonition.tldr .admonition-title::before { content: "\f0ea"; } + +/* info */ +.admonition.info { border-color: var(--cyan); background: var(--cyan-transparent); } +.admonition.info .admonition-title { background: rgba(128,255,234,.15); color: var(--cyan); } +.admonition.info .admonition-title::before { content: "\f05a"; } + +/* tip / hint / important */ +.admonition.tip,.admonition.hint,.admonition.important { border-color: var(--green); background: var(--green-transparent); } +.admonition.tip .admonition-title,.admonition.hint .admonition-title,.admonition.important .admonition-title { background: rgba(138,255,128,.15); color: var(--green); } +.admonition.tip .admonition-title::before,.admonition.hint .admonition-title::before,.admonition.important .admonition-title::before { content: "\f0eb"; } + +/* success / check / done */ +.admonition.success,.admonition.check,.admonition.done { border-color: var(--green); background: var(--green-transparent); } +.admonition.success .admonition-title,.admonition.check .admonition-title,.admonition.done .admonition-title { background: rgba(138,255,128,.15); color: var(--green); } +.admonition.success .admonition-title::before,.admonition.check .admonition-title::before,.admonition.done .admonition-title::before { content: "\f058"; } + +/* question / help / faq */ +.admonition.question,.admonition.help,.admonition.faq { border-color: var(--yellow); background: var(--yellow-transparent); } +.admonition.question .admonition-title,.admonition.help .admonition-title,.admonition.faq .admonition-title { background: rgba(255,255,128,.15); color: var(--yellow); } +.admonition.question .admonition-title::before,.admonition.help .admonition-title::before,.admonition.faq .admonition-title::before { content: "\f059"; } + +/* warning / caution / attention */ +.admonition.warning,.admonition.caution,.admonition.attention { border-color: var(--orange); background: var(--orange-transparent); } +.admonition.warning .admonition-title,.admonition.caution .admonition-title,.admonition.attention .admonition-title { background: rgba(255,202,128,.15); color: var(--orange); } +.admonition.warning .admonition-title::before,.admonition.caution .admonition-title::before,.admonition.attention .admonition-title::before { content: "\f071"; } + +/* failure / fail / missing */ +.admonition.failure,.admonition.fail,.admonition.missing { border-color: var(--pink); background: var(--pink-transparent); } +.admonition.failure .admonition-title,.admonition.fail .admonition-title,.admonition.missing .admonition-title { background: rgba(255,128,191,.15); color: var(--pink); } +.admonition.failure .admonition-title::before,.admonition.fail .admonition-title::before,.admonition.missing .admonition-title::before { content: "\f057"; } + +/* danger / error */ +.admonition.danger,.admonition.error { border-color: var(--red); background: var(--red-transparent); } +.admonition.danger .admonition-title,.admonition.error .admonition-title { background: rgba(255,149,128,.15); color: var(--red); } +.admonition.danger .admonition-title::before,.admonition.error .admonition-title::before { content: "\f0e7"; } + +/* bug */ +.admonition.bug { border-color: var(--red); background: var(--red-transparent); } +.admonition.bug .admonition-title { background: rgba(255,149,128,.15); color: var(--red); } +.admonition.bug .admonition-title::before { content: "\f188"; } + +/* example */ +.admonition.example { border-color: var(--purple); background: var(--purple-transparent); } +.admonition.example .admonition-title { background: rgba(149,128,255,.15); color: var(--purple); } +.admonition.example .admonition-title::before { content: "\f03a"; } + +/* quote / cite */ +.admonition.quote,.admonition.cite { border-color: var(--grey); background: rgba(65,69,88,.2); } +.admonition.quote .admonition-title,.admonition.cite .admonition-title { background: rgba(65,69,88,.3); color: var(--white); } +.admonition.quote .admonition-title::before,.admonition.cite .admonition-title::before { content: "\f10d"; } \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index f7ae246..7634f4b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,22 @@ def theme_dir(): return Path(mkdocs_dracula_theme.__file__).parent +@pytest.fixture(scope="session") +def css_source(): + return ( + Path(__file__).parent.parent + / "template" + / "assets" + / "css" + / "mkdocs.css" + ) + + +@pytest.fixture(scope="session") +def css_min(theme_dir): + return theme_dir / "assets" / "css" / "mkdocs.min.css" + + @pytest.fixture() def docs_dir(tmp_path): d = tmp_path / "docs" diff --git a/tests/test_admonition_colors.py b/tests/test_admonition_colors.py new file mode 100644 index 0000000..d5941da --- /dev/null +++ b/tests/test_admonition_colors.py @@ -0,0 +1,62 @@ +import re +import unittest + +import pytest + +EXPECTED_COLORS = { + "note": "--purple", + "example": "--purple", + "abstract": "--cyan", + "summary": "--cyan", + "tldr": "--cyan", + "info": "--cyan", + "tip": "--green", + "hint": "--green", + "important": "--green", + "success": "--green", + "check": "--green", + "done": "--green", + "question": "--yellow", + "help": "--yellow", + "faq": "--yellow", + "warning": "--orange", + "caution": "--orange", + "attention": "--orange", + "failure": "--pink", + "fail": "--pink", + "missing": "--pink", + "danger": "--red", + "error": "--red", + "bug": "--red", + "quote": "--grey", + "cite": "--grey", +} + + +class TestAdmonitionColors(unittest.TestCase): + @pytest.fixture(autouse=True) + def _inject_css(self, css_source): + self.css_source = css_source + + def test_source_css_uses_dracula_variables(self): + """Admonition styles must use CSS variables, not hardcoded hex values.""" + content = self.css_source.read_text() + start = content.find(".admonition {") + + self.assertNotEqual(start, -1, "Admonition base rule not found") + + admonition_section = content[start:] + + self.assertIsNone( + re.search(r":\s*#[0-9a-fA-F]{3,6}", admonition_section), + "Admonition styles must use CSS variables (var(--color)), not hardcoded hex values", + ) + + def test_expected_color_variables_present(self): + """Each admonition type must reference its expected Dracula color variable.""" + content = self.css_source.read_text() + + for admonition_type, color_var in EXPECTED_COLORS.items(): + pattern = rf"\.admonition\.{admonition_type}[^{{]*\{{[^}}]*{re.escape(color_var)}" + with self.subTest(admonition_type=admonition_type): + self.assertIsNotNone(re.search(pattern, content)) diff --git a/tests/test_admonition_coverage.py b/tests/test_admonition_coverage.py new file mode 100644 index 0000000..0fd7ad8 --- /dev/null +++ b/tests/test_admonition_coverage.py @@ -0,0 +1,72 @@ +import unittest + +import pytest + +ADMONITION_TYPES = [ + "note", + "abstract", + "summary", + "tldr", + "info", + "tip", + "hint", + "important", + "success", + "check", + "done", + "question", + "help", + "faq", + "warning", + "caution", + "attention", + "failure", + "fail", + "missing", + "danger", + "error", + "bug", + "example", + "quote", + "cite", +] + + +class TestAdmonitionCoverage(unittest.TestCase): + @pytest.fixture(autouse=True) + def _inject_css(self, css_source, css_min): + self.css_source = css_source + self.css_min = css_min + + def test_source_css_covers_all_types(self): + """Source CSS must define rules for every MkDocs admonition type.""" + content = self.css_source.read_text() + missing = [ + t for t in ADMONITION_TYPES if f".admonition.{t}" not in content + ] + + self.assertFalse( + missing, f"Missing admonition rules in source CSS: {missing}" + ) + + def test_min_css_covers_all_types(self): + """Minified CSS must define rules for every MkDocs admonition type.""" + content = self.css_min.read_text() + missing = [ + t for t in ADMONITION_TYPES if f".admonition.{t}" not in content + ] + + self.assertFalse( + missing, f"Missing admonition rules in minified CSS: {missing}" + ) + + def test_source_and_min_in_sync(self): + """Source and minified CSS must cover the exact same set of admonition types.""" + source = self.css_source.read_text() + minified = self.css_min.read_text() + + for admonition_type in ADMONITION_TYPES: + in_src = f".admonition.{admonition_type}" in source + in_min = f".admonition.{admonition_type}" in minified + with self.subTest(admonition_type=admonition_type): + self.assertEqual(in_src, in_min) diff --git a/tests/test_admonition_format.py b/tests/test_admonition_format.py new file mode 100644 index 0000000..b171898 --- /dev/null +++ b/tests/test_admonition_format.py @@ -0,0 +1,34 @@ +import unittest + +import pytest + + +class TestMinifiedCSSFormat(unittest.TestCase): + @pytest.fixture(autouse=True) + def _inject_css(self, css_min): + self.css_min = css_min + + def _admonition_section(self): + content = self.css_min.read_text() + start = content.find(".admonition{") + + self.assertNotEqual( + start, -1, "Admonition CSS not found in minified file" + ) + + return content[start:] + + def test_admonition_section_has_no_comments(self): + """The admonition block in mkdocs.min.css must not contain CSS comments.""" + self.assertNotIn("/*", self._admonition_section()) + + def test_admonition_section_has_no_indentation(self): + """The admonition block in mkdocs.min.css must not contain indented lines.""" + self.assertNotIn("\n ", self._admonition_section()) + + def test_admonition_uses_shorthand_values(self): + """Minified CSS should use shorthand values (e.g. .5rem not 0.5rem).""" + admonition_section = self._admonition_section() + + self.assertNotIn("0.5rem", admonition_section) + self.assertNotIn("0.75rem", admonition_section) diff --git a/tests/test_admonition_icons.py b/tests/test_admonition_icons.py new file mode 100644 index 0000000..b0a321e --- /dev/null +++ b/tests/test_admonition_icons.py @@ -0,0 +1,62 @@ +import unittest + +import pytest + +EXPECTED_ICONS = { + "note": r"\f040", + "abstract": r"\f0ea", + "summary": r"\f0ea", + "tldr": r"\f0ea", + "info": r"\f05a", + "tip": r"\f0eb", + "hint": r"\f0eb", + "important": r"\f0eb", + "success": r"\f058", + "check": r"\f058", + "done": r"\f058", + "question": r"\f059", + "help": r"\f059", + "faq": r"\f059", + "warning": r"\f071", + "caution": r"\f071", + "attention": r"\f071", + "failure": r"\f057", + "fail": r"\f057", + "missing": r"\f057", + "danger": r"\f0e7", + "error": r"\f0e7", + "bug": r"\f188", + "example": r"\f03a", + "quote": r"\f10d", + "cite": r"\f10d", +} + + +class TestAdmonitionIcons(unittest.TestCase): + @pytest.fixture(autouse=True) + def _inject_css(self, css_source, css_min): + self.css_source = css_source + self.css_min = css_min + + def test_tip_uses_lightbulb_not_fire_in_source(self): + """tip/hint/important must use fa-lightbulb-o (\\f0eb), not fa-fire (\\f06d).""" + content = self.css_source.read_text() + + self.assertIn(r"\f0eb", content) + self.assertNotIn(r"\f06d", content) + + def test_tip_uses_lightbulb_not_fire_in_min(self): + """Minified CSS: tip/hint/important must use fa-lightbulb-o (\\f0eb).""" + content = self.css_min.read_text() + + self.assertIn(r"\f0eb", content) + self.assertNotIn(r"\f06d", content) + + def test_all_icon_codepoints_present_in_source(self): + """Each admonition type must reference its expected Font Awesome codepoint.""" + content = self.css_source.read_text() + seen_codepoints = set(EXPECTED_ICONS.values()) + + for codepoint in seen_codepoints: + with self.subTest(codepoint=codepoint): + self.assertIn(codepoint, content)