A Python library to build Excel cell borders with simple range syntax, like VBA
Project description
xl-borders
A Python library that simplifies applying cell borders to openpyxl worksheets using Excel-style range strings (e.g. "A1:D5"), inspired by VBA's Range.Borders API.
Installation
# pip
pip install xl_borders
# uv
uv add xl_borders
Requires Python 3.13+ and openpyxl.
Quick Start
from openpyxl import Workbook
from xl_borders import set_border
wb = Workbook()
ws = wb.active
# Thin borders on all sides (default)
set_border(ws, "A1:D5")
# Thick red borders everywhere
set_border(ws, "A1:D5", style="thick", color="FF0000")
# Thick outer edges, thin inner grid
set_border(ws, "A1:D5", outline="thick", inside="thin")
# Compact numeric shorthand: thick outer, thin inner grid
set_border(ws, "A1:D5", custom=(3, 3, 3, 3, 1, 1))
# Override a single edge with shorthand (no Side import needed)
set_border(ws, "A1:D5", outline="medium", left=("double", "FF0000"))
wb.save("output.xlsx")
API
set_border(ws, cell_range, **kwargs)
Apply borders to a rectangular cell range.
Positional arguments:
| Argument | Type | Description |
|---|---|---|
ws |
Worksheet |
The openpyxl worksheet to modify. |
cell_range |
str |
Excel-style range string (e.g. "A1:D5", "B2"). |
Keyword-only arguments (in priority order, lowest to highest):
| Layer | Parameter | Type | Description |
|---|---|---|---|
| 1 | style |
str |
Base border style for all 6 sides. Default: "thin". |
| 1 | color |
str | None |
Base border color for all sides (hex, e.g. "FF0000"). |
| 2 | custom |
tuple[int, ...] | None |
Weight tuple in CSS-like order (see below). |
| 3 | outline |
str | None |
Style for all 4 outer edges. |
| 3 | inside |
str | None |
Style for inner horizontal and vertical lines. |
| 4 | horizontal |
str | None |
Style for top, bottom, and inner horizontal. |
| 4 | vertical |
str | None |
Style for left, right, and inner vertical. |
| 5 | left |
SideSpec |
Left edge (see Side shorthand below). |
| 5 | right |
SideSpec |
Right edge. |
| 5 | top |
SideSpec |
Top edge. |
| 5 | bottom |
SideSpec |
Bottom edge. |
| 5 | inner_horizontal |
SideSpec |
Inner horizontal lines. |
| 5 | inner_vertical |
SideSpec |
Inner vertical lines. |
Higher-layer parameters override lower ones. The color param is inherited by all layers unless a side specifies its own color.
Side shorthand (SideSpec)
Individual side parameters (left, right, top, bottom, inner_horizontal, inner_vertical) accept three forms:
| Form | Example | Result |
|---|---|---|
str |
left="thick" |
Side(style="thick"), inherits base color |
(str, str) tuple |
left=("thick", "FF0000") |
Side(style="thick", color="FF0000") |
Side |
left=Side(style="thick", ...) |
Used as-is (full control) |
This eliminates the need to import Side for most use cases.
The custom tuple
A compact way to set all 6 border positions with numeric weights:
(top, right, bottom, left, inner_horizontal, inner_vertical)
Weight mapping: 0 = none, 1 = thin, 2 = medium, 3 = thick.
The tuple length must be 4 or 6. A 4-element tuple sets the outer edges only; inner borders default to 0 (none). The base color is applied to all sides created by custom.
# 6-element: thick outer, thin horizontal grid, medium vertical grid
set_border(ws, "A1:D5", custom=(3, 3, 3, 3, 1, 2))
# 4-element: medium outer edges, no inner grid
set_border(ws, "A1:D5", custom=(2, 2, 2, 2))
# With color: all red
set_border(ws, "A1:D5", custom=(3, 3, 3, 3, 1, 1), color="FF0000")
Examples
All borders in one color
set_border(ws, "A1:D5", style="thick", color="0000FF")
Box border (outline only, no inner grid)
set_border(
ws, "A1:D5",
outline="medium",
inner_horizontal=Side(style=None),
inner_vertical=Side(style=None),
)
Different horizontal and vertical weights
set_border(ws, "A1:D5", horizontal="thin", vertical="thick")
Override a single edge with shorthand
# String shorthand (inherits base color)
set_border(ws, "A1:D5", outline="medium", color="0000FF", left="double")
# Tuple shorthand (own color overrides base)
set_border(ws, "A1:D5", outline="medium", left=("double", "FF0000"))
Using custom with overrides
Higher-priority layers refine on top of custom:
set_border(
ws, "A1:D5",
custom=(3, 2, 3, 2, 1, 1),
color="0000FF",
left=("double", "FF0000"), # override left: double red
)
Available border styles
Any style string accepted by openpyxl's Side(style=...):
thin, medium, thick, double, dashed, mediumDashed, dotted, mediumDashDot, dashDot, mediumDashDotDot, dashDotDot, hair, slantDashDot
Development
# Install dependencies
uv sync
# Run tests
uv run pytest Test/ -v
License
MIT - See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xl_borders-0.1.1.tar.gz.
File metadata
- Download URL: xl_borders-0.1.1.tar.gz
- Upload date:
- Size: 5.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da1054f7dbb4392cea25b58522ee09d53628b3fae54bcc90c9650c4dc5dbabe9
|
|
| MD5 |
d86e2de59cf1d7f81652e4e662695c30
|
|
| BLAKE2b-256 |
c40cdb0b698e2f4e366680eb850a66c8d6cf88b59987a05cf154ae6a12e662d7
|
File details
Details for the file xl_borders-0.1.1-py3-none-any.whl.
File metadata
- Download URL: xl_borders-0.1.1-py3-none-any.whl
- Upload date:
- Size: 6.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf85561533fe3440f35fc5b3b9bde8c832d1156b04038fc98dbea9b7eef02c91
|
|
| MD5 |
60e0c8813955c169feb5a69a8d9aa468
|
|
| BLAKE2b-256 |
bf54256d0eed9379dca10e63a97904f30f9f6530699c5d4d7552d298a33fb709
|