
Proofread Markdown
- 24 installs
- 3.1k repo stars
- Updated August 5, 2026
- flutter/website
Proofreads Markdown files against the Google developer documentation style guide for line length, voice, word choice, and heading style.
About
Provides a checklist to transform technical Markdown into clear, style-guide-compliant text covering line wrapping, active voice, forbidden terms, and heading and list formatting. A developer uses it when editing Markdown docs to follow Google style guidelines.
- Wrap lines under 80 chars and never touch front matter
- Enforce active voice, present tense, second person, and sentence-case headings
Proofread Markdown by the numbers
- 24 all-time installs (skills.sh)
- Ranked #973 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/flutter/website --skill proofread-markdownAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 24 |
|---|---|
| repo stars | ★ 3.1k |
| Last updated | August 5, 2026 |
| Repository | flutter/website ↗ |
What it does
Proofreads Markdown files against the Google developer documentation style guide for line length, voice, word choice, and heading style.
Files
Proofreading Markdown
Overview
Help us transform technical text into clear, concise, and machine-readable Markdown. We follow the Google Developer Documentation Style Guide.
Workflow
1. Check line length
- [ ] Wrap lines semantically and keep them generally under 80 characters using
scripts/wrap_lines.dart
- [ ] Never touch the front matter.
Do not modify the content between the first two --- lines at the top of the file.
- [ ] Verify that the file was wrapped correctly,
preserving headings and code blocks
2. Fix voice and tone
- [ ] Use active voice.
- [ ] Use present tense.
- [ ] Use second person ("you"). No "we" or "I".
3. Check word choice
- [ ] Replace forbidden terms: "e.g.", "i.e.", "etc.", "should", "would",
"could", "via", "may".
- [ ] Use Oxford comma and American spelling.
4. Check style of headings
- [ ] Use Sentence case for headings.
- If you change a heading, add an id after the heading like this:
"## Configure the project {: #project-configuration }"
- [ ] Put a blank line after all headings.
5. Check style of lists
- [ ] Numbered lists MUST use
1.for every item to allow easy reordering. - [ ] How-to sections must have active voice in headings.
- No: "## Project Configuration"
- Yes: "## Configure the project"
6. Use less words in sections with steps
- [ ] Ask the user if they would like the steps to be more concise.
If yes, update the steps to be brief. Keep it short, simple, and accurate.
import 'dart:io';
/// A script to wrap markdown lines at 80 columns, preserving headings and code blocks.
void main(List<String> arguments) {
if (arguments.isEmpty) {
print('Usage: dart wrap_lines.dart <file_path>');
exit(1);
}
final filePath = arguments[0];
final file = File(filePath);
if (!file.existsSync()) {
print('File not found: $filePath');
exit(1);
}
final content = file.readAsStringSync();
final wrappedContent = wrapMarkdown(content);
file.writeAsStringSync(wrappedContent);
print('Successfully wrapped markdown for $filePath');
}
String wrapMarkdown(String content, {int width = 80}) {
final lines = content.split('\n');
final output = <String>[];
bool inCodeBlock = false;
bool inFrontMatter = false;
bool firstLine = true;
for (final line in lines) {
final stripped = line.trim();
if (firstLine) {
firstLine = false;
if (stripped == '---') {
inFrontMatter = true;
output.add(line);
continue;
}
}
if (inFrontMatter) {
output.add(line);
if (stripped == '---') {
inFrontMatter = false;
}
continue;
}
// Handle code blocks
if (stripped.startsWith('```') || stripped.startsWith('~~~')) {
inCodeBlock = !inCodeBlock;
output.add(line);
continue;
}
if (inCodeBlock) {
output.add(line);
continue;
}
// Handle headings
if (stripped.startsWith('#')) {
output.add(line);
continue;
}
// Handle empty lines
if (stripped.isEmpty) {
output.add('');
continue;
}
// Handle lists or quotes (matching python script's simple approach)
if (stripped.startsWith('-') ||
stripped.startsWith('*') ||
RegExp(r'^\d+\.').hasMatch(stripped)) {
output.addAll(wrapText(line, width));
continue;
}
// Normal text paragraph
output.addAll(wrapText(line, width));
}
return output.join('\n');
}
List<String> wrapText(String text, int width) {
final leadingSpace = RegExp(r'^\s*').stringMatch(text) ?? '';
final content = text.trim();
if (content.isEmpty) return [leadingSpace];
// Split by spaces that follow punctuation (.,;:!?)
// We use a lookbehind to keep the punctuation with the preceding chunk.
final chunks = content.split(RegExp(r'(?<=[.,;:!?])\s+'));
final lines = <String>[];
var currentLine = leadingSpace;
for (final chunk in chunks) {
// If the chunk itself is longer than width, fallback to word wrapping for this chunk
if (chunk.length > width) {
// Flush current line if not empty
if (currentLine != leadingSpace) {
lines.add(currentLine);
currentLine = leadingSpace;
}
// Word wrap the chunk
final words = chunk.split(RegExp(r'\s+'));
for (final word in words) {
final space = currentLine == leadingSpace ? '' : ' ';
if ((currentLine.length + space.length + word.length) > width) {
if (currentLine != leadingSpace) {
lines.add(currentLine);
currentLine = leadingSpace + word;
} else {
lines.add(leadingSpace + word);
currentLine = leadingSpace;
}
} else {
currentLine += space + word;
}
}
continue;
}
final space = currentLine == leadingSpace ? '' : ' ';
if ((currentLine.length + space.length + chunk.length) > width) {
if (currentLine != leadingSpace) {
lines.add(currentLine);
}
currentLine = leadingSpace + chunk;
} else {
currentLine += space + chunk;
}
}
if (currentLine != leadingSpace) {
lines.add(currentLine);
}
return lines;
}