DEFAULT_COLUMN_WIDTH_CHARACTERS
DEFAULT_COLUMN_WIDTH_CHARACTERS: 8.43
Excel's default column width, in pixels.
The file says "8.43 characters" and means the width of the digit zero in the
workbook's Normal font, plus five pixels of padding. For Calibri 11 that
digit is seven pixels wide, which is where the familiar 64 comes from.
XLSX_VIEW_CSS
XLSX_VIEW_CSS: "\n.jo-xl {\n --jo-xl-line: #d0d7de;\n --jo-xl-header-bg: #f5f5f5;\n --jo-xl-header-fg: #575757;\n --jo-xl-surface: #ffffff;\n /*\n * Black, and not a softer near-black.\n *\n * A cell whose font names no colour is drawn in the window text colour, and\n * in Excel that is pure black — every reference reading of every workbook\n * says #000000. A gentler grey reads better on its own but it is not what the\n * document looks like, and next to a cell that *does* set black the\n * difference shows as two shades of text in one column.\n */\n --jo-xl-text: #000000;\n --jo-xl-accent: #217346;\n --jo-xl-selection: rgba(33, 115, 70, 0.12);\n\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--jo-xl-surface);\n color: var(--jo-xl-text);\n font-family: Calibri, 'Segoe UI', system-ui, sans-serif;\n font-size: 11pt;\n --jo-xl-row-header-width: 46px;\n}\n\n/* ------------------------------------------------------------- formula bar */\n\n.jo-xl__formula-bar {\n display: flex;\n align-items: stretch;\n gap: 0;\n flex: 0 0 auto;\n border-bottom: 1px solid var(--jo-xl-line);\n background: var(--jo-xl-surface);\n font-size: 12px;\n}\n\n.jo-xl__name-box {\n width: 140px;\n padding: 4px 8px;\n border-right: 1px solid var(--jo-xl-line);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.jo-xl__fx {\n padding: 4px 10px;\n color: var(--jo-xl-header-fg);\n font-style: italic;\n border-right: 1px solid var(--jo-xl-line);\n}\n\n.jo-xl__formula {\n flex: 1;\n padding: 4px 8px;\n white-space: pre;\n overflow: hidden;\n text-overflow: ellipsis;\n font-family: Consolas, 'SF Mono', monospace;\n}\n\n/* -------------------------------------------------------------------- grid */\n\n.jo-xl__host {\n flex: 1 1 auto;\n min-height: 0;\n position: relative;\n}\n\n/*\n * The grid does not select text; it selects cells.\n *\n * Dragging over a range is the sheet's own gesture, and while the browser was\n * also allowed to drag a text selection through it the two ran at once — and\n * the browser's ran through the *document* order, which on an absolutely\n * positioned grid has nothing to do with what is on the screen. The reader drew\n * a tidy green rectangle and got a ragged blue one over cells nowhere near it.\n *\n * Text selection is not given up, only asked for: double-clicking a cell opens\n * it, and the --reading rule below hands that one cell back to the browser so\n * the words in it can be selected and copied on their own. Escape, or a click\n * anywhere else, closes it again. It is what Excel does, and it is the only\n * arrangement in which both gestures can mean something.\n */\n.jo-xl__grid {\n user-select: none;\n -webkit-user-select: none;\n display: grid;\n width: 100%;\n height: 100%;\n overflow: hidden;\n position: relative;\n background: var(--jo-xl-surface);\n}\n\n/*\n * The grid is a tab stop, and its focus is shown by the selection.\n *\n * A ring around the whole sheet says only that the grid has focus, which is the\n * least useful thing a reader could be told; the selection rectangle says\n * *where* the focus is, which is what they need and what every spreadsheet\n * shows. So the ring is suppressed and the selection stands in for it — which\n * means the selection must always be drawn, and it is.\n */\n.jo-xl__grid:focus,\n.jo-xl__grid:focus-visible {\n outline: none;\n}\n\n.jo-xl__grid--rtl { direction: rtl; }\n\n.jo-xl__corner,\n.jo-xl__colhead,\n.jo-xl__rowhead,\n.jo-xl__pane {\n position: relative;\n overflow: hidden;\n min-width: 0;\n min-height: 0;\n}\n\n.jo-xl__scroller {\n overflow: auto;\n /* Scrolling repaints the whole pane; telling the compositor in advance keeps\n a large sheet at one frame per scroll instead of one per repaint. */\n will-change: scroll-position;\n}\n\n.jo-xl__sizer {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n}\n\n.jo-xl__canvas,\n.jo-xl__lines {\n position: absolute;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n}\n\n/*\n * The headers are part of the grid, so they scale with it.\n *\n * Relative to the grid's own size, which the view sets from the workbook's base\n * format multiplied by the zoom. A literal pixel size here was one of the\n * lengths that did not scale: the header boxes shrank with the columns and the\n * letters in them did not.\n */\n.jo-xl__corner,\n.jo-xl__colhead,\n.jo-xl__rowhead {\n background: var(--jo-xl-header-bg);\n color: var(--jo-xl-header-fg);\n font-size: 0.75em;\n user-select: none;\n}\n\n.jo-xl__corner {\n border-right: 1px solid var(--jo-xl-line);\n border-bottom: 1px solid var(--jo-xl-line);\n cursor: pointer;\n}\n\n.jo-xl__colhead { border-bottom: 1px solid var(--jo-xl-line); }\n.jo-xl__rowhead { border-right: 1px solid var(--jo-xl-line); }\n\n/* The freeze line, drawn on the panes that border it. */\n.jo-xl__pane--tl,\n.jo-xl__pane--tr { border-bottom: 1px solid #a0a0a0; }\n.jo-xl__pane--tl,\n.jo-xl__pane--bl { border-right: 1px solid #a0a0a0; }\n\n.jo-xl__header {\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n border-right: 1px solid var(--jo-xl-line);\n border-bottom: 1px solid var(--jo-xl-line);\n overflow: hidden;\n cursor: default;\n}\n\n.jo-xl__header--selected {\n background: var(--jo-xl-selection);\n color: var(--jo-xl-accent);\n font-weight: 600;\n}\n\n/* ------------------------------------------------------------------- cells */\n\n.jo-xl__cell {\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n align-items: flex-end;\n box-sizing: border-box;\n /*\n * Excel's own gap between a cell's edge and its text, and it is a length like\n * any other on the grid: it scales. Written as a property the view sets from\n * the zoom rather than as a literal, because a literal is what it was — and a\n * three-pixel constant left unscaled shifts every column of a zoomed sheet by\n * three pixels more than it should, which is small, systematic, and exactly\n * the kind of thing nobody finds by looking.\n */\n padding: 0 var(--jo-xl-cell-pad, 3px);\n overflow: hidden;\n white-space: pre;\n line-height: 1.15;\n /* Layout containment only. Paint containment would clip the text of a cell\n that overflows into its empty neighbours, which is behaviour Excel has and\n a grid has to keep. */\n contain: layout;\n}\n\n/* Text that spills into empty neighbours, as Excel lets it. */\n.jo-xl__cell--overflow {\n overflow: visible;\n z-index: 2;\n max-width: var(--jo-xl-spill);\n}\n\n.jo-xl__cell--merged { z-index: 1; }\n\n/*\n * The one cell the reader opened for its text.\n *\n * Raised above its neighbours so that a selection dragged across it cannot\n * wander into the cells drawn over it, and given a caret so that the browser's\n * own word and line selection work inside it.\n */\n.jo-xl__cell--reading {\n user-select: text;\n -webkit-user-select: text;\n cursor: text;\n z-index: 3;\n overflow: visible;\n}\n\n/*\n * A link is blue only when the workbook says so.\n *\n * Excel does not colour a cell because it has a hyperlink on it; it colours it\n * because the cell wears the Hyperlink style, which is written into the file\n * like any other. Files that libraries produce often carry the link without the\n * style, and Excel draws those in plain black — so painting every link blue\n * shows a document that does not exist. The pointer stays, and the underline\n * comes back under the cursor, where it costs the picture nothing.\n */\n.jo-xl__cell--link {\n cursor: pointer;\n}\n\n.jo-xl__cell--link:hover {\n text-decoration: underline;\n}\n\n/* A comment is marked the way Excel marks it: a triangle in the corner. */\n.jo-xl__cell--commented::after {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n border: 3px solid transparent;\n border-top-color: #d1343b;\n border-right-color: #d1343b;\n}\n\n.jo-xl__gridline {\n position: absolute;\n top: 0;\n left: 0;\n background: var(--jo-xl-line);\n pointer-events: none;\n}\n\n.jo-xl__gridline--h { height: 1px; }\n.jo-xl__gridline--v { width: 1px; }\n\n.jo-xl__selection {\n position: absolute;\n top: 0;\n left: 0;\n border: 2px solid var(--jo-xl-accent);\n background: var(--jo-xl-selection);\n pointer-events: none;\n z-index: 3;\n}\n\n/* --------------------------------------------------- conditional formatting */\n\n.jo-xl__bar {\n position: absolute;\n left: 0;\n top: 2px;\n bottom: 2px;\n border-radius: 1px;\n opacity: 0.75;\n pointer-events: none;\n}\n\n/* The value sits above its data bar, as it does in Excel. An absolutely\n positioned bar paints over ordinary flow content, so the text is given a\n position of its own rather than the bar a negative index — which would put\n it behind the cell's own background. */\n.jo-xl__value {\n position: relative;\n z-index: 1;\n min-width: 0;\n overflow: inherit;\n text-overflow: inherit;\n white-space: inherit;\n}\n\n.jo-xl__icon {\n font-size: 0.85em;\n margin-right: 3px;\n flex: 0 0 auto;\n}\n\n/* The button of a filter header or of a list validation. Excel draws it as\n part of the cell, at the right edge, over whatever the cell holds. */\n\n.jo-xl__button {\n position: absolute;\n right: 1px;\n top: 50%;\n transform: translateY(-50%);\n /* Above the cell's own text, which is drawn after it and would otherwise\n take the click meant for the button. */\n z-index: 4;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15px;\n height: 15px;\n border: 1px solid var(--jo-xl-line);\n border-radius: 2px;\n background: var(--jo-xl-header-bg);\n color: var(--jo-xl-header-fg);\n font-size: 9px;\n line-height: 1;\n /* It used to be a picture of a button. It opens a menu now, so it takes the\n click that lands on it rather than passing it through to the cell. */\n cursor: pointer;\n}\n\n.jo-xl__button:hover {\n border-color: var(--jo-xl-accent);\n}\n\n.jo-xl__button--filtering {\n color: var(--jo-xl-accent);\n font-weight: 700;\n}\n\n/*\n * The note a commented cell shows.\n *\n * Excel's colours, because a reader recognises them: the pale yellow paper and\n * the thin grey rule are what a note has looked like for thirty years, and a\n * box in the viewer's own palette would read as part of the viewer rather than\n * as something written into the document.\n */\n.jo-xl__note {\n position: absolute;\n z-index: 22;\n max-width: 320px;\n max-height: 240px;\n overflow: auto;\n padding: 6px 8px;\n border: 1px solid #b0b0a0;\n background: #ffffe1;\n box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.25);\n color: #000000;\n font-size: 12px;\n line-height: 1.35;\n /* The pointer must be able to reach it: a note longer than its box is one\n the reader has to scroll. */\n pointer-events: auto;\n}\n\n.jo-xl__note-author {\n font-weight: 700;\n margin-bottom: 2px;\n}\n\n.jo-xl__note-text {\n white-space: pre-wrap;\n}\n\n/*\n * The find bar, and the marks it leaves on the sheet.\n *\n * Floated over the top-right of the grid rather than pushed into the layout:\n * opening a search should not move the rows the reader was looking at.\n */\n.jo-xl__find {\n position: absolute;\n z-index: 25;\n top: 6px;\n right: 18px;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px 6px;\n border: 1px solid var(--jo-xl-line);\n border-radius: 4px;\n background: var(--jo-xl-surface);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);\n font-size: 12px;\n}\n\n.jo-xl__find-input {\n width: 180px;\n padding: 3px 6px;\n border: 1px solid var(--jo-xl-line);\n border-radius: 3px;\n font: inherit;\n color: inherit;\n background: var(--jo-xl-surface);\n}\n\n.jo-xl__find-count {\n min-width: 72px;\n color: var(--jo-xl-header-fg);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.jo-xl__find-button {\n width: 22px;\n height: 22px;\n border: 1px solid var(--jo-xl-line);\n border-radius: 3px;\n background: var(--jo-xl-header-bg);\n color: inherit;\n font: inherit;\n line-height: 1;\n cursor: pointer;\n}\n\n.jo-xl__find-button:hover {\n border-color: var(--jo-xl-accent);\n}\n\n/*\n * A cell a search matched, and the one being stood on.\n *\n * The mark has to survive whatever the cell already wears — a table band, a\n * conditional format, its own fill — so it is an outline and a wash rather than\n * a background, which any of those would win against.\n */\n.jo-xl__cell--match {\n box-shadow: inset 0 0 0 1px var(--jo-xl-accent);\n background-image: linear-gradient(\n color-mix(in srgb, var(--jo-xl-accent) 18%, transparent),\n color-mix(in srgb, var(--jo-xl-accent) 18%, transparent)\n );\n}\n\n.jo-xl__cell--match-current {\n box-shadow: inset 0 0 0 2px var(--jo-xl-accent);\n background-image: linear-gradient(\n color-mix(in srgb, var(--jo-xl-accent) 38%, transparent),\n color-mix(in srgb, var(--jo-xl-accent) 38%, transparent)\n );\n}\n\n/*\n * The strip on a header's trailing edge that resizes it.\n *\n * Wider than the line it sits on: a two-pixel target is one a reader misses,\n * and every spreadsheet gives this a few pixels of slack. It stays inside the\n * header, which clips what overflows it, and sits above the header's own label\n * so that a press on it is a resize rather than a selection.\n */\n.jo-xl__grip {\n position: absolute;\n z-index: 5;\n}\n\n.jo-xl__grip--column {\n top: 0;\n bottom: 0;\n right: 0;\n width: 6px;\n cursor: col-resize;\n}\n\n.jo-xl__grip--row {\n left: 0;\n right: 0;\n bottom: 0;\n height: 6px;\n cursor: row-resize;\n}\n\n/* Where the drag will land, drawn while it is held. */\n.jo-xl__guide {\n position: absolute;\n z-index: 30;\n background: var(--jo-xl-accent);\n pointer-events: none;\n}\n\n.jo-xl__guide--column {\n top: 0;\n bottom: 0;\n width: 1px;\n}\n\n.jo-xl__guide--row {\n left: 0;\n right: 0;\n height: 1px;\n}\n\n/*\n * The menu a filter button opens.\n *\n * Above the panes rather than inside one: a pane clips what overflows it, and a\n * menu that lists twenty values would be cut off at the first row boundary.\n */\n.jo-xl__filter-menu {\n position: absolute;\n z-index: 20;\n min-width: 160px;\n max-width: 280px;\n border: 1px solid var(--jo-xl-line);\n border-radius: 4px;\n background: var(--jo-xl-surface);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);\n font-size: 12px;\n overflow: hidden;\n}\n\n.jo-xl__filter-list {\n max-height: 240px;\n overflow: auto;\n padding: 4px 0;\n}\n\n.jo-xl__filter-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 8px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.jo-xl__filter-item:hover {\n background: var(--jo-xl-header-bg);\n}\n\n.jo-xl__filter-item span {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.jo-xl__filter-actions {\n display: flex;\n gap: 4px;\n padding: 6px;\n border-top: 1px solid var(--jo-xl-line);\n}\n\n.jo-xl__filter-button {\n flex: 1;\n padding: 3px 6px;\n border: 1px solid var(--jo-xl-line);\n border-radius: 3px;\n background: var(--jo-xl-header-bg);\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n\n.jo-xl__filter-button:hover {\n border-color: var(--jo-xl-accent);\n}\n\n/* A shape's own outline, drawn under whatever text it holds. */\n\n.jo-xl__outline {\n position: absolute;\n inset: 0;\n overflow: visible;\n pointer-events: none;\n}\n\n/* The outline gutter: a bracket per group, a button on its summary row. */\n\n.jo-xl__outline-bracket {\n position: absolute;\n top: 0;\n left: 0;\n width: 1px;\n background: var(--jo-xl-header-fg);\n opacity: 0.55;\n}\n\n.jo-xl__outline-button {\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 11px;\n height: 11px;\n border: 1px solid var(--jo-xl-header-fg);\n background: var(--jo-xl-bg);\n color: var(--jo-xl-header-fg);\n font-size: 9px;\n line-height: 1;\n}\n\n/* The axis of a data bar: where zero falls, when it falls inside the cell. */\n\n.jo-xl__bar-axis {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n pointer-events: none;\n}\n\n/* A sparkline fills its cell and sits behind whatever the cell holds. */\n\n.jo-xl__sparkline {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n\n/* --------------------------------------------------------------- drawings */\n\n.jo-xl__drawing {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 4;\n pointer-events: none;\n overflow: hidden;\n}\n\n/*\n * A picture fills its box, and is distorted if the box says so.\n *\n * Excel stretches a picture into the rectangle it was given — a:stretch is what\n * the file says and stretching is what it means — so a picture whose box was\n * dragged out of proportion is drawn out of proportion. Fitting it inside\n * instead keeps the shape but leaves bands of empty sheet on two sides and puts\n * the picture somewhere the author did not, which is the more visible error of\n * the two and the harder one to explain.\n */\n.jo-xl__drawing img {\n width: 100%;\n height: 100%;\n object-fit: fill;\n}\n\n.jo-xl__drawing--placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px dashed var(--jo-xl-line);\n background: color-mix(in srgb, var(--jo-xl-header-bg) 70%, transparent);\n color: var(--jo-xl-header-fg);\n font-size: 11px;\n text-align: center;\n padding: 4px;\n}\n\n.jo-xl__drawing--shape {\n white-space: pre-wrap;\n font-size: 11px;\n}\n\n/* ------------------------------------------------------------------- tabs */\n\n.jo-xl__tabs {\n display: flex;\n gap: 1px;\n padding: 3px 6px 0;\n border-top: 1px solid var(--jo-xl-line);\n background: var(--jo-xl-header-bg);\n overflow-x: auto;\n flex: 0 0 auto;\n}\n\n.jo-xl__tab {\n appearance: none;\n border: 1px solid transparent;\n border-bottom: none;\n border-radius: 4px 4px 0 0;\n background: transparent;\n color: inherit;\n padding: 4px 12px;\n cursor: pointer;\n white-space: nowrap;\n font: inherit;\n font-size: 12px;\n border-bottom: 3px solid var(--jo-xl-tab-color, transparent);\n}\n\n.jo-xl__tab:hover { background: rgba(0, 0, 0, 0.04); }\n\n.jo-xl__tab[aria-selected='true'] {\n background: var(--jo-xl-surface);\n border-color: var(--jo-xl-line);\n border-bottom-color: var(--jo-xl-tab-color, var(--jo-xl-surface));\n font-weight: 600;\n color: var(--jo-xl-accent);\n}\n\n.jo-xl__tab--hidden { opacity: 0.55; font-style: italic; }\n\n.jo-xl__empty {\n padding: 32px;\n text-align: center;\n color: var(--jo-xl-header-fg);\n font-size: 13px;\n}\n\n@media (prefers-color-scheme: dark) {\n .jo-xl {\n --jo-xl-line: #3d444d;\n --jo-xl-header-bg: #21262d;\n --jo-xl-header-fg: #9198a1;\n --jo-xl-surface: #0d1117;\n --jo-xl-text: #e6edf3;\n --jo-xl-accent: #3fb950;\n --jo-xl-selection: rgba(63, 185, 80, 0.16);\n }\n\n .jo-xl__tab:hover { background: rgba(255, 255, 255, 0.06); }\n}\n\n@media print {\n .jo-xl__formula-bar,\n .jo-xl__tabs { display: none; }\n\n .jo-xl__scroller { overflow: visible; }\n}\n"
The renderer's own stylesheet.
Only the chrome and the structure are here. Everything that comes from the
workbook — fonts, fills, borders, alignment — is emitted as generated classes
at render time, because it differs per file and there are a few dozen of them
per sheet rather than a fixed set.
xlsxView
xlsxView: ViewPlugin<XlsxDocument>