Tăng cường text-wrap: pretty
với ngắt dòng thông minh
Bạn có bao giờ để ý đến chữ “a” nhỏ bé treo ở cuối tiêu đề không? 😱 text-wrap: pretty
đã giúp các đoạn văn trở nên thông minh hơn — nhưng nó không nhận thức ngôn ngữ. Trong nhiều truyền thống biên tập, bạn không muốn ngắt dòng sau các từ nhỏ (mạo từ, giới từ, liên từ), hoặc giữa một danh hiệu và tên theo sau, hoặc giữa các chữ cái viết tắt và họ.
Bài viết này đề xuất một nâng cấp nhỏ, nhận thức ngôn ngữ cho việc ngắt dòng — và cung cấp một polyfill nhỏ mà bạn có thể thử trong 30 giây.
CodePen: https://codepen.io/jlorenzetti/pen/zxvXewX
Nội dung chính
text-wrap: pretty
cải thiện việc ngắt dòng, nhưng nó không nhận thức ngôn ngữ.- Polyfill nhỏ này thêm một lớp keo biên tập: giữ các cặp rõ ràng lại với nhau (ví dụ: Ông Smith, Hình 2, 20 °C, 1900–2000, J. K. Rowling) và tùy chọn giữ các từ nhỏ gắn liền với từ tiếp theo nơi đó là quy tắc biên tập thông thường.
- Không phụ thuộc, chỉ một lần duy nhất trên DOM, không đo lường bố cục.
Giải quyết vấn đề của pretty
text-wrap: pretty
tập trung vào tránh các dòng cuối ngắn và có thể đưa ra các lựa chọn ngắt dòng thông minh hơn gần cuối đoạn văn (các chi tiết thay đổi tùy theo trình duyệt). Nhưng nó không quan tâm đến ngữ nghĩa biên tập — nó không biết rằng Hình và 2 thuộc về nhau, hoặc rằng nhiều truyền thống biên tập tránh việc ngắt sau các từ chức năng ngắn.
Polyfill này thêm vừa đủ ngữ nghĩa lên trên pretty
, mà không tái triển khai bố cục. Hãy nghĩ về nó như một lớp “không ngắt ở đây” cho các quy tắc biên tập đã được thiết lập.
Nâng cấp trong một phút
- Phạm vi: chỉ các phần tử có
text-wrap
đã tính toán làpretty
. - Quét: một lần duy nhất qua các nút văn bản (không đọc bố cục, không tái bố cục).
- Quyết định: khớp các mẫu nhận thức ngôn ngữ:
- Quy tắc an toàn (luôn bật): danh hiệu + tên, chữ cái viết tắt + họ, nhãn + số, số + đơn vị, § + số, khoảng số (WORD JOINER xung quanh dấu gạch ngang).
- Từ nhỏ (theo địa phương): gắn các từ chức năng ngắn chỉ khi đây là một quy tắc biên tập phổ biến.
- Keo: thay thế một khoảng trắng bình thường bằng NBSP (
U+00A0
) hoặc chèn WORD JOINER (U+2060
) khi phù hợp. - Idempotent & an toàn: bỏ qua URL/email, không vượt qua các phần tử inline theo mặc định, không chạm vào
pre/code/...
.
Bắt đầu nhanh
HTML thuần (không xây dựng)
html
<article class="typo" lang="en">
<p>Tham khảo Hình 2 để biết chi tiết.</p>
</article>
<style>
.typo { text-wrap: pretty; hyphens: auto; }
/* Các UA cũ hơn không có `text-wrap: pretty`: tham gia vào lớp keo */
@supports not (text-wrap: pretty) {
.typo { --text-wrap-preferences: minor-words; }
}
</style>
<script type="module">
import { init, registerLanguage } from 'https://cdn.jsdelivr.net/npm/text-wrap-minor-words@0.3.1/dist/lite.mjs';
import en from 'https://cdn.jsdelivr.net/npm/text-wrap-minor-words@0.3.1/locales/en.json' assert { type: 'json' };
registerLanguage('en', en);
const ctrl = init({ languages: ['en'] });
ctrl.process();
</script>
Thay thế
en
bằng địa phương khác nếu cần. Để hỗ trợ nhiều địa phương, hãy nhập/đăng ký nhiều tệp JSON theo cách tương tự.
Node / bundler (ESM)
bash
npm i text-wrap-minor-words
javascript
import { init, registerLanguage } from 'text-wrap-minor-words/lite';
import en from 'text-wrap-minor-words/locales/en.json';
registerLanguage('en', en);
const ctrl = init({ languages: ['en'], observe: true });
ctrl.process();
css
<style>
.typo { text-wrap: pretty; hyphens: auto; }
@supports not (text-wrap: pretty) {
.typo { --text-wrap-preferences: minor-words; }
}
</style>
Bạn cũng có thể tải bản toàn cầu không lite:
<script src="https://cdn.jsdelivr.net/npm/text-wrap-minor-words@0.3.1/dist/index.global.js"></script>
(bao gồm dữ liệu địa phương tích hợp cho các thử nghiệm nhanh; ưu tiên lite trong sản xuất).
Tùy chọn hiển thị tiếng Anh (tùy chọn)
Theo mặc định, các từ nhỏ được tắt cho văn bản tiếng Anh. Đoạn mã này chỉ kích hoạt chúng chỉ trong các bối cảnh hiển thị (tiêu đề).
HTML thuần (không xây dựng)
html
<article class="typo" lang="en">
<h1>Từ một cuốn sách đến trình duyệt: hướng dẫn thực hành về kiểu chữ trên web</h1>
</article>
<style>
.typo { text-wrap: pretty; hyphens: auto; }
/* Kích hoạt lớp keo minor-words chỉ cho tiêu đề tiếng Anh (hiển thị) */
.typo :is(h1,h2,h3,h4,h5,h6):lang(en) {
--text-wrap-preferences: minor-words; /* bật */
--text-wrap-minor-threshold: 1; /* keo sau các mã token 1 ký tự */
--text-wrap-minor-stoplist: "of to in on at for by a I"; /* điều chỉnh theo hướng dẫn phong cách của bạn */
}
@supports not (text-wrap: pretty) {
.typo { --text-wrap-preferences: minor-words; }
}
</style>
<script type="module">
import { init, registerLanguage } from 'https://cdn.jsdelivr.net/npm/text-wrap-minor-words@0.3.1/dist/lite.mjs';
import en from 'https://cdn.jsdelivr.net/npm/text-wrap-minor-words@0.3.1/locales/en.json' assert { type: 'json' };
registerLanguage('en', en);
const ctrl = init({ languages: ['en'], context: 'display' }); // giới hạn xử lý chỉ trong các bối cảnh hiển thị
ctrl.process();
</script>
Bundler / ESM
bash
npm i text-wrap-minor-words
javascript
import { init, registerLanguage } from 'text-wrap-minor-words/lite';
import en from 'text-wrap-minor-words/locales/en.json';
registerLanguage('en', en);
const ctrl = init({ languages: ['en'], context: 'display' });
ctrl.process();
css
.typo { text-wrap: pretty; hyphens: auto; }
.typo :is(h1,h2,h3,h4,h5,h6):lang(en) {
--text-wrap-preferences: minor-words;
--text-wrap-minor-threshold: 1;
--text-wrap-minor-stoplist: "of to in on at for by a I";
}
@supports not (text-wrap: pretty) {
.typo { --text-wrap-preferences: minor-words; }
}
Mẹo: ưu tiên
:lang(en)
hơn[lang="en"]
để tiêu đề khớp với ngôn ngữ thừa hưởng từ container.
Mặc định & ghi chú ngôn ngữ (tóm tắt)
- Quy tắc an toàn (bật trong mọi ngôn ngữ): danh hiệu + tên (Ông Smith, Tiến sĩ Müller), chữ cái viết tắt + họ (J. K. Rowling), nhãn + số (Hình 2, S. 12), số + đơn vị (20 °C, 10 km), § + số, khoảng số (1900–2000 với
U+2060
quanh dấu gạch ngang). - Từ nhỏ: bật theo mặc định trong các ngôn ngữ La Tinh/Slavic/Hy Lạp; tắt theo mặc định trong tiếng Anh/Đức/Hà Lan (bạn có thể tham gia cho hiển thị).
Locale | Keo 1 ký tự | Ví dụ từ danh sách dừng mặc định* |
---|---|---|
fr | ✓ | de, du, le, la, les, un, une… |
it | ✓ | di, da, in, su, con, per… |
pl | ✓ | w, z, do, na, po… |
ru | ✓ | в, к, с, на, по… |
el | ✓ | σε, το, τη, οι… |
en | — | (quy tắc an toàn theo mặc định; tham gia cho hiển thị) |
* Các danh sách đầy đủ và cấu hình trong tập dữ liệu ngôn ngữ.
Hiệu suất & khả năng truy cập
- O(n) qua các nút văn bản. Regex được biên dịch sẵn theo địa phương. Không có phép đo bố cục.
- Thoát sớm cho các địa phương trung tính (ví dụ: en/de/nl khi các từ nhỏ bị tắt).
- Đọc màn hình: NBSP/WORD JOINER không hiển thị với AT; phát hiện URL/email ngăn ngừa việc keo không mong muốn.
Lưu ý
- Vượt qua inline: hiện tại chúng tôi không vượt qua các phần tử inline (ví dụ:
a <em>từ</em>
). Chúng tôi có thể đánh giá một tùy chọn tham gia trong tương lai. - CJK/RTL: không nhắm đến hiện tại (các truyền thống ngắt dòng khác nhau).
- Không phải là quy tắc ngữ pháp: điều này mã hóa các quy ước biên tập, không phải quy tắc ngôn ngữ. Các mặc định là bảo thủ; ghi đè khi hướng dẫn phong cách của bạn khác biệt.
Khía cạnh quy định (tại sao điều này có thể thuộc về CSS)
Polyfill này khám phá một mở rộng nhận thức ngôn ngữ nhỏ cho text-wrap
. Nếu việc sử dụng trên thực tế chứng minh nó hữu ích rộng rãi và rủi ro thấp, một hình dạng có thể sẽ là một tùy chọn dành riêng (ví dụ: một chế độ con “minor-words”) cho phép các UA áp dụng các gợi ý “không ngắt ở đây” ít tranh cãi này.
Repo bao gồm một giải thích với mục tiêu không, dữ liệu địa phương, và các bài kiểm tra. Nếu bạn có mẫu sản xuất hoặc các trường hợp cạnh tranh, hãy chia sẻ chúng — chúng chính xác là những gì một đề xuất trong tương lai cần.
Hãy thử và cho tôi biết điều gì bị hỏng
- Demo trực tiếp: https://jlorenzetti.github.io/text-wrap-minor-words/
- Repo: https://github.com/jlorenzetti/text-wrap-minor-words
- CodePen: https://codepen.io/jlorenzetti/pen/zxvXewX
Bạn có một trường hợp cạnh tranh (đặc biệt trong hiển thị tiếng Anh, hoặc một địa phương mới)? Mở một vấn đề hoặc để lại một bình luận — các ví dụ và ảnh chụp màn hình là rất quý giá.