A Ruby library for syntax highlighting markdown code with beautiful, customizable output.
OhHighMark provides colorful and readable markdown syntax visualization by tokenizing and rendering markdown with appropriate CSS classes. It displays each line in a table row with line numbers, ensuring perfect alignment and preserving all whitespace.
- Table-based layout - Each line in its own table row with separate cells for line numbers and code
- Perfect alignment - Line numbers stay aligned with code content, even with long lines
- Whitespace preservation - Uses
white-space: preto maintain exact spacing and indentation - Non-selectable line numbers - Prevents copy/paste issues
- Comprehensive markdown support - Headings, text formatting, code, links, lists, tables, quotes, and more
- Inline markdown in lists - Bold, italic, links, and other inline syntax work within list items
- Customizable styling - Pre-built CSS with CSS variables for easy theming
- Semantic HTML - Clean, semantic markup with
data-line-numberon rows - Flexible rules - Customizable tokenization rules via DSL
OhHighMark supports all standard markdown elements:
Text Formatting:
- Bold:
**text** - Italic:
_text_ - Bold-Italic:
***text*** Strikethrough:~~text~~
Headings: # H1 through ###### H6
Code:
- Inline:
`code` - Fenced blocks:
```...```
Links:
- Standard:
[text](url) - Auto-links:
<http://url>
Lists:
- Unordered:
- item,* item,+ item - Nested:
- nested item - With inline markdown:
- **bold** with [link](url)
Tables:
| Column 1 | Column 2 |
| -------- | -------- |
| Value 1 | Value 2 |Other:
- Horizontal rules:
--- - Block quotes:
> quote - Escaped characters:
\*,\[,\],\\
Add to your Gemfile:
gem 'ohhighmark'Then run:
$ bundle installOr install directly:
$ gem install ohhighmarkrequire 'ohhighmark'
# Use the helper in your views (Rails, Middleman, etc.)
include OhHighMark::Helper
# In your ERB view:
<%= highlight_markdown do %>
# Your Markdown Here
This is **bold** and this is _italic_.
- List item with [link](https://example.com)
- Item with `inline code`
<% end %>require 'ohhighmark'
markdown = <<~MD
# Welcome
This is **bold** and ~~strikethrough~~.
MD
# Create highlighter and process
highlighter = OhHighMark::Highlighter.new
html_output = highlighter.process_lines_with_linenums(markdown)# Define custom markdown rules
ruleset = OhHighMark::RuleSet.new
OhHighMark::MarkdownRules.apply(ruleset)
# Add custom rules if needed
ruleset.add(:custom_pattern, /your_regex/, :inline)
# Create tokenizer and renderer
tokenizer = OhHighMark::Tokenizer.new(ruleset.rules)
tokens = tokenizer.tokenize(markdown_text)
renderer = OhHighMark::Renderer.new
html = renderer.render(tokens)OhHighMark includes a pre-built CSS stylesheet with CSS custom properties (CSS variables) for easy customization.
Rails (Asset Pipeline / Sprockets):
In application.css:
/*
*= require ohhighmark/ohhighmark
*/Or in application.scss:
@import 'ohhighmark/ohhighmark';Rails (Propshaft / Importmap):
Copy the stylesheet:
$ cp $(bundle show ohhighmark)/app/assets/stylesheets/ohhighmark/ohhighmark.css app/assets/stylesheets/Link in your layout:
<%= stylesheet_link_tag "ohhighmark" %>Middleman:
Copy the stylesheet:
$ cp $(bundle show ohhighmark)/app/assets/stylesheets/ohhighmark/ohhighmark.css source/stylesheets/Then import it in your main stylesheet.
Standalone / Other Frameworks:
Find the stylesheet in the gem:
$ bundle show ohhighmark
# Copy app/assets/stylesheets/ohhighmark/ohhighmark.css to your projectOverride CSS variables after including the OhHighMark stylesheet:
:root {
/* Text and background */
--ohm-text-color: #ffffff;
--ohm-background-color: #1a1a1a;
--ohm-linenos-color: #666666;
--ohm-linenos-bg-color: #1a1a1a;
/* Syntax colors */
--ohm-yellow: #ffff00; /* H1 headings */
--ohm-orange: #ff8000; /* H2 headings */
--ohm-purple: #ff00ff; /* H3 headings */
--ohm-cyan: #00ffff; /* H4-H6 headings */
--ohm-blue: #0080ff; /* Links */
--ohm-intense-blue: #0040ff; /* Link hover */
--ohm-green: #00ff00; /* Quotes, lists */
--ohm-gray: #808080; /* Horizontal rules */
--ohm-grayish: #999999; /* Table borders */
/* Table highlighting */
--ohm-table-bg: rgba(255, 248, 197, 0.2);
/* Font */
--ohm-monospace-font: "Courier New", monospace;
}Dark Theme Example:
:root {
--ohm-text-color: #e0e0e0;
--ohm-background-color: #0d1117;
--ohm-linenos-bg-color: #0d1117;
--ohm-linenos-color: #6e7681;
--ohm-blue: #4a9eff;
--ohm-green: #7cfc00;
--ohm-yellow: #ffd700;
--ohm-orange: #ff6b35;
}Light Theme Example:
:root {
--ohm-text-color: #333333;
--ohm-background-color: #ffffff;
--ohm-linenos-bg-color: #f6f8fa;
--ohm-linenos-color: #57606a;
--ohm-blue: #0969da;
--ohm-green: #1a7f37;
--ohm-yellow: #b8860b;
--ohm-orange: #d2691e;
}OhHighMark generates semantic HTML with clean structure:
<div class="ohhighmark">
<table class="highlight">
<tbody>
<!-- Regular content row -->
<tr data-line-number="1">
<td class="blob-num">1</td>
<td class="blob-code"><span class="md-h1"># Heading</span></td>
</tr>
<!-- Row with text -->
<tr data-line-number="2">
<td class="blob-num">2</td>
<td class="blob-code">Some text with <span class="md-bold">**bold**</span></td>
</tr>
<!-- Table row (has table-line class) -->
<tr class="table-line" data-line-number="3">
<td class="blob-num">3</td>
<td class="blob-code"><span class="md-table-pipe">|</span> Col 1 <span class="md-table-pipe">|</span> Col 2 <span class="md-table-pipe">|</span></td>
</tr>
</tbody>
</table>
</div>Key Structure Points:
data-line-numberattribute on<tr>for easy row targeting.blob-numcell contains line number.blob-codecell contains formatted content.table-lineclass added to rows that are part of markdown tables- Markdown syntax wrapped in
<span>elements with.md-*classes
Container & Layout:
.ohhighmark- Main container divtable.highlight- Table element containing all lines.blob-num- Line number cell (user-select: noneprevents copying).blob-code- Code content cell (useswhite-space: pre).table-line- Added to<tr>for markdown table rows
Markdown Syntax Classes:
.md-h1through.md-h6- Heading levels 1-6.md-bold- Bold text (**text**).md-italic- Italic text (_text_).md-em- Bold-italic emphasis (***text***).md-strikethrough- Strikethrough (~~text~~).md-code- Inline code (`code`).md-link- Standard links ([text](url)).md-autolink- Auto-linked URLs (<http://url>).md-li- List item markers.md-hr- Horizontal rules (---).md-fence- Fenced code block delimiters.md-codeblock- Content within fenced code blocks.md-table-pipe- Table pipe characters (|).md-table-dash- Table separator rows (---).md-quote- Block quotes (> text)
The .blob-code cell MUST have white-space: pre to preserve whitespace and prevent wrapping:
.ohhighmark td.blob-code {
white-space: pre; /* REQUIRED - preserves all whitespace */
font-family: ui-monospace, monospace;
line-height: 20px;
}Target rows by line number:
// Select a specific line
const row = document.querySelector('[data-line-number="5"]');
// Highlight a line
row.style.backgroundColor = 'rgba(255, 255, 0, 0.2)';
// Get all table rows
const tableRows = document.querySelectorAll('tr[data-line-number]');Bug reports and pull requests are welcome!
$ git clone https://github.com/viacoffee/ohhighmark.git
$ cd ohhighmark
$ bundle install
$ rspec # Run testsThis gem is available as open source under the terms of the MIT License.