Files
gitlab-foss/app/assets/stylesheets/framework/typography.scss
2025-07-18 16:07:43 +00:00

985 lines
19 KiB
SCSS

/**
* Apply Markup (Markdown/AsciiDoc) typography
*
*/
.md {
position: relative;
z-index: 1;
@apply gl-text-default;
word-wrap: break-word;
[dir='auto'] {
text-align: initial;
}
*:first-child {
@apply gl-mt-0;
}
> :last-child {
@apply gl-mb-0;
}
p {
@apply gl-text-default gl-m-0 gl-mb-5;
> code {
font-weight: inherit;
@apply gl-break-all gl-whitespace-break-spaces;
}
}
.media-container {
display: inline-flex;
flex-direction: column;
@apply gl-mb-2;
video {
max-width: 100%;
}
}
img {
max-width: 100%;
vertical-align: baseline;
}
img[width][height] {
object-fit: contain;
object-position: top;
height: auto;
}
img.lazy {
min-width: 200px;
min-height: 100px;
@apply gl-bg-disabled;
}
img.js-lazy-loaded,
img.emoji {
min-width: inherit;
min-height: inherit;
background-color: inherit;
}
details {
@apply gl-mb-5;
> *:not(summary) {
@apply gl-ml-5;
}
&:not(:has(summary)):not([open]) {
@apply gl-font-bold;
}
}
details[open] summary {
@apply gl-mb-5;
}
// Shift the anchor link for a heading inside a details summary to the left
// to make room for the summary toggle
details summary {
@apply gl-font-bold;
h1, h2, h3, h4, h5, h6 {
a.anchor {
@apply -gl-ml-7;
}
}
}
summary > * {
@apply gl-inline-block gl-mb-0;
}
// make default paragraph of summary text behave nicely with the summary marker
summary > p {
max-width: calc(100% - 1rem);
}
// Single code lines should wrap
code {
@apply gl-font-monospace;
white-space: pre-wrap;
// Safari
word-wrap: break-word;
overflow-wrap: break-word;
word-break: keep-all;
}
mark {
@include str-highlighted;
}
// the z-index of the mark is set to -1, so it's below the table cells
// unless they have their own stacking context
td:has(mark), th:has(mark) {
isolation: isolate;
}
// Fixed headings are default for markdown
h1 {
@apply gl-heading-1-fixed gl-mt-7 gl-pb-2 gl-border-b;
}
h2:not(:where(.crud-header) h2) {
@apply gl-heading-2-fixed gl-mt-6 gl-pb-2 gl-border-b;
}
h3 {
@apply gl-heading-3-fixed gl-mt-6;
}
h4 {
@apply gl-heading-4-fixed gl-mt-5;
}
h5 {
@apply gl-heading-5-fixed gl-mt-5;
}
h6 {
@apply gl-heading-6-fixed gl-mt-5;
}
blockquote,
.blockquote {
@apply gl-text-size-reset gl-text-subtle;
@apply gl-py-3 gl-pl-5 gl-my-3 gl-mx-0;
@apply gl-border-l gl-border-l-4 gl-border-strong;
&:dir(rtl) {
@apply gl-border-l-0 gl-border-r gl-border-r-4;
}
p {
@apply gl-text-inherit gl-leading-[1.5];
&:last-child {
margin: 0;
}
}
// ensure quoted lists are styled the same as normal lists
> ul {
list-style-type: disc;
ul {
list-style-type: circle;
ul {
list-style-type: square;
}
}
}
}
hr {
@apply gl-border-default;
margin: 10px 0;
}
@keyframes gl-table-shadow {
0% {
box-shadow:
inset -16px 0 14px -14px var(--gl-shadow-color-default),
inset 0 0 0 0 var(--gl-shadow-color-default);
}
5% {
box-shadow:
inset -16px 0 14px -14px var(--gl-shadow-color-default),
inset 16px 0 14px -14px var(--gl-shadow-color-default);
}
95% {
box-shadow:
inset -16px 0 14px -14px var(--gl-shadow-color-default),
inset 16px 0 14px -14px var(--gl-shadow-color-default);
}
100% {
box-shadow:
inset 0 0 0 0 var(--gl-shadow-color-default),
inset 16px 0 14px -14px var(--gl-shadow-color-default);
}
}
// Fix border bleed.
.gl-table-shadow:not(:has(+ .glql-load-more)) {
clip-path: inset(0 round 0 0 1px 1px);
}
.gl-table-shadow {
overflow-x: auto;
overflow-y: hidden;
animation: gl-table-shadow $gl-easing-out-cubic;
animation-timeline: scroll(self inline);
// Needs to stay white because we use mix-blend-mode
// to make it work in both light and darkmode.
background-color: var(--gl-color-neutral-0);
> table:not(.code) {
display: table;
overflow-x: initial;
mix-blend-mode: multiply;
// strech is not available yet, so we have to rely
// on the vendor prefixed ones below
min-width: strech;
/* stylelint-disable-next-line value-no-vendor-prefix */
min-width: -webkit-fill-available;
/* stylelint-disable-next-line value-no-vendor-prefix */
min-width: -moz-available;
}
}
table:not(.code) {
margin: 16px 0;
@apply gl-text-default;
border: 0;
width: auto;
display: block;
overflow-x: auto;
tbody {
@apply gl-bg-default;
}
td,
th {
@apply gl-border;
}
tr {
th {
@apply gl-border-b;
}
}
&.grid-none {
> thead > tr {
> th {
border-bottom-width: 0;
border-right-width: 0;
border-left-width: 0;
&:first-child {
border-left-width: 1px;
}
&:last-child {
border-right-width: 1px;
}
}
}
> tbody {
> tr > td {
border-width: 0;
&:first-child {
border-left-width: 1px;
}
&:last-child {
border-right-width: 1px;
}
}
> tr:last-child > td {
border-bottom-width: 1px;
}
}
}
&.grid-rows {
> thead > tr > th,
> tbody > tr > td {
border-right-width: 0;
border-left-width: 0;
}
> thead > tr {
> th:first-child {
border-left-width: 1px;
}
> th:last-child {
border-right-width: 1px;
}
}
> tbody > tr {
> td {
border-left-width: 0;
border-right-width: 0;
}
> td:first-child {
border-left-width: 1px;
}
> td:last-child {
border-right-width: 1px;
}
}
}
&.grid-cols {
> thead > tr > th {
border-bottom-width: 0;
}
> tbody > tr > td {
border-top-width: 0;
border-bottom-width: 0;
}
> tbody > tr:last-child > td {
border-bottom-width: 1px;
}
}
&.frame-sides {
> thead > tr > th {
border-top-width: 0;
}
> tbody > tr:last-child > td {
border-bottom-width: 0;
}
}
&.frame-topbot,
&.frame-ends {
> thead > tr > th:first-child,
> tbody > tr > td:first-child {
border-left-width: 0;
}
> thead > tr > th:last-child,
> tbody > tr > td:last-child {
border-right-width: 0;
}
}
&.frame-none {
> thead > tr > th {
border-top-width: 0;
}
> tbody > tr:last-child > td {
border-bottom-width: 0;
}
> thead > tr > th:first-child,
> tbody > tr > td:first-child {
border-left-width: 0;
}
> thead > tr > th:last-child,
> tbody > tr > td:last-child {
border-right-width: 0;
}
}
&.stripes-all tr,
&.stripes-odd tr:nth-of-type(odd),
&.stripes-even tr:nth-of-type(even),
&.stripes-hover tr:hover {
@apply gl-bg-subtle;
}
}
table:dir(rtl) th {
text-align: right;
}
pre {
@apply gl-mb-5;
line-height: 1.6em;
overflow-x: auto;
@apply gl-rounded-base;
// Multi-line code blocks should scroll horizontally
code {
white-space: pre;
// Safari
word-wrap: normal;
overflow-wrap: normal;
}
&.plain-readme {
background: none;
border: 0;
padding: 0;
margin: 0;
font-size: 14px;
}
}
dd {
margin-left: $gl-padding;
}
ul,
ol {
padding: 0;
@apply gl-m-0 gl-mb-5;
// Lists embedded in other lists can be "loose" or "tight"
// Remove bottom margin for all lists (default for tight lists)
ul,
ol {
@apply gl-mb-0;
}
// Loose lists need bottom margin added back
p ~ ol,
p ~ ul {
@apply gl-mb-5;
}
}
ul:dir(rtl),
ol:dir(rtl) {
margin: 3px 28px 3px 0 !important;
}
> ul {
list-style-type: disc;
ul {
list-style-type: circle;
ul {
list-style-type: square;
}
}
}
ul.checklist,
ul.none,
ol.none,
ul.no-bullet,
ol.no-bullet,
ol.unnumbered,
ul.unstyled,
ol.unstyled {
list-style-type: none;
li {
margin-left: 0;
}
}
li {
line-height: 1.6em;
margin-left: 25px;
padding-left: 3px;
/* Normalize the bullet position on webkit. */
/* stylelint-disable-next-line media-feature-name-no-vendor-prefix */
@media screen and (-webkit-min-device-pixel-ratio: 0) {
margin-left: 28px;
padding-left: 0;
}
}
ul.task-list {
> li.task-list-item {
list-style-type: none;
position: relative;
min-height: 22px;
padding-inline-start: 32px;
margin-inline-start: 0 !important;
> p > input.task-list-item-checkbox,
> input.task-list-item-checkbox {
position: absolute;
inset-inline-start: $gl-padding-8;
inset-block-start: 3px;
}
}
}
ul[data-type=taskList] input[type=checkbox],
ul.task-list .task-list-item-checkbox {
all: unset;
@apply gl-w-5 gl-h-5 gl-cursor-pointer gl-box-border gl-rounded-base focus-visible:gl-focus;
background-color: var(--gl-control-background-color-default);
border: 1px solid var(--gl-control-border-color-default);
// Ensure size of the target for pointer inputs is at least 24 pixels to satisfy WCAG 2.5.8.
&::before {
content: '';
margin-top: calc(-#{$gl-spacing-scale-2} - 1px);
margin-left: calc(-#{$gl-spacing-scale-2} - 1px);
@apply gl-absolute gl-z-1 gl-w-6 gl-h-6 gl-bg-transparent;
}
&:not(:disabled):hover {
border-color: var(--gl-control-border-color-hover);
}
&:not(:disabled):focus {
@apply gl-focus;
border-color: var(--gl-control-border-color-focus);
}
&:checked {
background-color: var(--gl-control-background-color-selected-default);
border-color: var(--gl-control-border-color-selected-default);
&::after {
content: '';
@apply gl-absolute gl-z-2 gl-w-5 gl-h-5 gl-mt-[-1px] gl-ml-[-1px] gl-rounded-base;
mask: url("#{$gl-icon-check}") center center no-repeat;
background-color: var(--gl-control-indicator-color-selected);
}
@media (forced-colors: active) {
background-color: LinkText; // stylelint-disable-line scale-unlimited/declaration-strict-value
border-color: LinkText; // stylelint-disable-line scale-unlimited/declaration-strict-value
}
}
&:not(:disabled):checked:hover {
background-color: var(--gl-control-background-color-selected-hover);
border-color: var(--gl-control-border-color-selected-hover);
}
&:not(:disabled):checked:focus {
background-color: var(--gl-control-background-color-selected-focus);
border-color: var(--gl-control-border-color-selected-focus);
}
&:disabled {
background-color: var(--gl-control-background-color-disabled);
border-color: var(--gl-control-border-color-disabled);
@apply gl-cursor-not-allowed gl-text-disabled;
}
&:disabled:checked {
background-color: var(--gl-control-background-color-disabled);
border-color: var(--gl-control-border-color-disabled);
&::after {
background-color: var(--gl-control-indicator-color-disabled);
}
}
}
li.inapplicable {
// for a single line list item, no paragraph (tight list)
> s {
@apply gl-text-disabled;
}
// additional blocks, other than paragraphs
> div {
text-decoration: line-through;
@apply gl-text-disabled;
}
// because of the embedded checkbox, putting line-through on the entire
// paragraph causes the space between the checkbox and the text to have the
// line-through. Targeting just the `s` fixes this
> p:first-of-type > s {
@apply gl-text-disabled;
}
> p:not(:first-of-type) {
text-decoration: line-through;
@apply gl-text-disabled;
}
.drag-icon {
@apply gl-fill-icon-default;
}
// If an inapplicable task list item contains labels or <kbd>s, strike them
// out as well.
.gl-label, kbd {
text-decoration: line-through;
text-decoration-color: var(--gl-text-color-disabled);
vertical-align: text-bottom;
}
}
a.gfm-gollum-wiki-page {
&::before {
margin-right: 4px;
vertical-align: -2px;
@include gl-dark-invert-keep-hue;
content: url('sprite_icons/document.svg');
}
}
a.with-attachment-icon,
a[href*='/uploads/'],
a[href*='storage.googleapis.com/google-code-attachments/'] {
&::before {
margin-right: 4px;
vertical-align: -2px;
@include gl-dark-invert-keep-hue;
content: url('sprite_icons/paperclip.svg');
}
}
a[href*='/wikis/'],
a[href*='/uploads/'],
a[href*='storage.googleapis.com/google-code-attachments/'] {
&.no-attachment-icon {
&::before {
display: none;
}
}
}
/* Link to current header. */
h1,
h2,
h3,
h4,
h5,
h6 {
$anchor-offset: 20px;
a.anchor {
margin-left: calc(-1 * $anchor-offset);
text-decoration: none;
outline: none;
position: absolute;
width: $anchor-offset;
&::after {
@include gl-dark-invert-keep-hue;
content: url('icon_anchor.svg');
visibility: hidden;
}
}
&:hover > a.anchor::after {
visibility: visible;
}
> a.anchor:focus::after {
visibility: visible;
outline: auto;
}
}
.big {
font-size: larger;
}
figcaption,
.small {
font-size: smaller;
}
.underline {
text-decoration: underline;
}
.overline {
text-decoration: overline;
}
.line-through {
text-decoration: line-through;
}
// Custom Font Awesome styles that render emojis in asciidoc
.fa {
display: inline-block;
font-style: normal;
font-size: 14px;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.admonitionblock td.icon [class^='fa icon-'] {
font-size: 2em;
}
.admonitionblock td.icon .icon-warning::before {
content: '';
}
.admonitionblock td.icon .icon-important::before {
content: '';
}
.admonitionblock td.icon .icon-tip::before {
content: '💡';
}
.admonitionblock td.icon .icon-note::before {
content: '📌';
}
.admonitionblock td.icon .icon-caution::before {
content: '🔥';
}
.admonitionblock, .sidebarblock, .openblock, .exampleblock {
margin: $gl-padding 0 $gl-padding;
}
.sidebarblock {
@apply gl-bg-strong;
padding: $gl-padding-12 $gl-padding-24;
border-radius: $gl-border-radius-large;
p {
margin: 0;
}
}
.exampleblock > div:nth-of-type(1) {
@apply gl-border;
border-width: $gl-border-size-3;
@apply gl-rounded-base;
padding: $gl-padding-12 $gl-padding-24;
p {
margin: 0;
}
}
.fa-square-o::before {
content: '\2610';
}
.fa-check-square-o::before {
content: '\2611';
}
.admonitionblock td.icon {
width: 1%;
}
.gl-dropdown-item {
margin: 0;
padding: 0;
line-height: 1rem;
}
/* AsciiDoc(tor) built-in alignment roles */
.text-left {
text-align: left !important;
}
.text-right {
text-align: right !important;
}
.text-center {
text-align: center !important;
}
.text-justify {
text-align: justify !important;
}
@include email-code-block;
&.md-child-content-text-subtle {
@apply gl-text-subtle;
p,
h1,
h2,
h3,
h4,
h5,
table:not(.code) {
@apply gl-text-subtle;
}
}
/* Vertically align badges with icons */
.gl-badge:has(> svg) {
position: relative;
bottom: -3px;
}
}
/**
* Links
*
*/
a:focus-visible {
@include gl-focus($outline: true, $outline-offset: $outline-width);
}
/**
* Headers
*
*/
body {
-webkit-text-shadow: $body-text-shadow 0 0 1px;
}
.page-title {
margin: $gl-spacing-scale-4 0;
line-height: 1.3;
&.with-button {
line-height: 34px;
}
}
.page-title-empty {
margin: 12px 0;
line-height: 1.3;
font-size: 1.25em;
font-weight: $gl-font-weight-bold;
}
.light-header {
font-weight: $gl-font-weight-bold;
}
/** CODE **/
pre {
position: relative;
@apply gl-font-monospace;
display: block;
padding: $gl-padding-8 $input-horizontal-padding;
margin: 0 0 $gl-padding-8;
font-size: $gl-font-size;
word-break: break-all;
word-wrap: break-word;
@apply gl-text-default gl-bg-subtle gl-border;
border-radius: $border-radius-small;
// Select only code elements that will have the copy code button
.markdown-code-block & {
padding: $input-horizontal-padding;
}
}
.monospace {
@apply gl-font-monospace;
}
.weight-normal {
font-weight: $gl-font-weight-normal;
}
.commit-sha,
.ref-name {
@apply gl-font-monospace;
font-size: 95%;
}
.git-revision-dropdown .dropdown-content li:not(.dropdown-menu-empty-item) a {
@apply gl-font-monospace;
font-size: 95%;
word-break: break-all;
}
/**
* Textareas intended for GFM
*
*/
textarea.js-gfm-input {
@apply gl-font-monospace;
font-size: $gl-font-size;
}
h1,
h2,
h3,
h4 {
small {
@apply gl-text-heading;
}
}
.text-right-md {
@include media-breakpoint-up(md) {
text-align: right;
}
}
.text-right-lg {
@include media-breakpoint-up(lg) {
text-align: right;
}
}
.idiff.deletion {
background: var(--code-old-inline-diff-background-color, $line-removed-dark);
}
.idiff.addition {
background: var(--code-new-inline-diff-background-color, $line-added-dark);
}
/**
* form text input i.e. search bar, comments, forms, etc.
*/
input,
textarea {
&::placeholder {
color: var(--gl-control-placeholder-color);
}
}
wbr {
display: inline-block;
}
// The font used in Monaco editor - Web IDE, Snippets, single file editor
:root {
--code-editor-font: #{$monospace-font};
}
/**
* FOOTNOTES
*/
section.footnotes {
position: relative;
padding-top: $gl-spacing-scale-5;
margin-top: $gl-spacing-scale-7;
font-size: $gl-font-size-12;
&::before {
content: '';
width: 16rem;
max-width: 100%;
@apply gl-border-t;
position: absolute;
top: 0;
left: 0;
}
ol > li {
margin-inline-start: $gl-spacing-scale-6;
p {
margin-block-end: 0;
}
}
// prevent backref character from rendering as emoji
.footnote-backref gl-emoji {
font-family: inherit;
font-size: 0.875em;
pointer-events: none;
}
}