Repository navigation
Font Library: revised API design #57616
Description
Companion issue to #55278
This issue outlines a proposed design for font management REST API endpoints.
Font Family
A font family with the settings used in theme.json format. A font family may have child font faces that are part of the family.
GET wp/v2/font-families
List the installed font families.
Font family theme.json properties are nested in font_family_settings to keep camel-cased properties out of the root level of the response, where snake-casing is required.
Request:
GET wp/v2/font-families
Response:
[
{
// ID of wp_font_family post
"id": 1,
"theme_json_version": 2,
// Array of child font face ids
"font_faces": [ 1007, 1008 ],
// theme.json settings used under `settings.typography.fontFamilies`
"font_family_settings": {
"name": "Piazzolla",
"fontFamily": "Piazzolla, serif",
"slug": "piazzolla",
"preview": "https://my-wp-site.com/wp-content/fonts/previews/piazzolla-400-normal.svg"
},
// See below for example `_links`
"_links": { ... }
},
{
"id": 2,
"theme_json_version": 2,
"font_faces": [ 1009, 1010 ],
"font_family_settings": {
"name": "Lobster",
"fontFamily": "Lobster, sans-serif",
"slug": "lobster",
"preview": "https://my-wp-site.com/wp-content/fonts/previews/lobster-400-normal.svg"
},
"_links": { ... }
},
...
]GET wp/v2/font-families/<id>
Get a font family by id.
Request:
GET wp/v2/font-families/1
Response:
{
"id": 1,
"theme_json_version": 2,
"font_faces": [ 258 ],
"font_family_settings": {
"name": "Piazzolla",
"fontFamily": "Piazzolla, serif",
"slug": "piazzolla"
},
"_links": {
"self": [{
"href": "https://my-wp-site.com/wp-json/wp/v2/font-families/1"
}],
"collection": [{
"href": "https://my-wp-site.com/wp-json/wp/v2/font-families"
}],
"font_faces": [{
"embeddable": true,
"href": "https://my-wp-site.com/wp-json/wp/v2/font-families/1/font-faces"
}]
}
}Using embeddings to get font face information, as well.
Request:
GET wp/v2/font-families/1?_embed=true
GET wp/v2/font-families/1?_embed=font_faces
Response:
{
"id": 1,
"theme_json_version": 2,
"font_faces": [ 258 ],
"font_family_settings": {
"name": "Piazzolla",
"fontFamily": "Piazzolla, serif",
"slug": "piazzolla",
"preview": "https://my-wp-site.com/wp-content/fonts/previews/piazzolla-400-normal.svg"
},
"_links": { ... },
"_embedded": {
"font_faces": [
{
"id": 258,
"theme_json_version": 2,
"parent": "1",
"font_face_settings": {
"fontStyle": "normal",
"fontWeight": "400",
"fontFamily": "Piazzolla",
"src": [
"https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.ttf",
"https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.woff2"
],
"preview": "https://my-wp-site.com/wp-content/fonts/previews/piazzolla-400-normal.svg"
}
}
]
}
}POST wp/v2/font-families
Create a new font family.
Request:
POST wp/v2/font-families
{
"theme_json_version": 2,
"font_family_settings": {
"name": "Piazzolla",
"fontFamily": "Piazzolla, serif",
"slug": "piazzolla"
}
}Response:
{
"id": 1,
"theme_json_version": 2,
"font_faces": [],
"font_family_settings": {
"name": "Piazzolla",
"fontFamily": "Piazzolla, serif",
"slug": "piazzolla",
"preview"
},
"_links": { ... }
}POST wp/v2/font-families/<id>
Update font family by id. It merges the received data with the existing data in the font family post (the same way the wp/v2/posts endpoint works).
Request:
POST wp/v2/font-families/1
{
"font_family_settings": {
"fontFamily": "Piazzolla, times, serif",
}
}Response:
{
"id": 1,
"theme_json_version": 2,
"font_faces": [ 258 ],
"font_family_settings": {
"name": "Piazzolla",
"fontFamily": "Piazzolla, times, serif",
"slug": "piazzolla"
},
"_links": { ... }
}DELETE wp/v2/font-families/<id>
Delete font family by id. All child font faces and assets are deleted.
Request:
DELETE wp/v2/font-families/1
Response:
{
"code": "rest_trash_not_supported",
"message": "The post does not support trashing. Set 'force=true' to delete.",
"data": { "status": 501 }
}Request:
DELETE /font-families/1?force=true
Response:
{
"deleted": true,
"previous": {
"id": 1,
"theme_json_version": 2,
"font_faces": [ 258 ],
"font_family_settings": {
"name": "Piazzolla",
"fontFamily": "Piazzolla, times, serif",
"slug": "piazzolla",
}
}
}Font Face
Font faces that are used within a font family.
Font face theme.json properties are nested in font_face_settings to keep camel-cased properties out of the root level of the response, where snake-casing is required.
The API design uses the wp/v2/posts/<id>/revisions endpoints as a model, since that is also a nested route with a parent/child relationship.
GET wp/v2/font-families/<id>/font-faces
Get all of the font faces for a font family.
Request:
GET wp/v2/font-families/1/font-faces
Response:
[
{
// ID of the wp_font_face custom post type
"id": 1835,
"theme_json_version": 2,
// ID of parent wp_font_family post
"parent": "1",
// theme.json settings used under `settings.typography.fontFamilies.fontFace`
"font_face_settings": {
"fontStyle": "normal",
"fontWeight": "400",
"fontFamily": "Piazzolla",
// src can be a string for a single font asset, or an array for multiple assets
"src": [
"https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.ttf",
"https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.woff2"
],
"preview": "https://my-wp-site.com/wp-content/fonts/previews/piazzolla-400-normal.svg"
},
"_links": { ... }
},
...
]GET wp/v2/font-families/<id>/font-faces/<id>
Get a single font face for a font family.
Request:
GET wp/v2/font-families/1/font-faces/1835
Response:
{
"id": 1835,
"theme_json_version": 2,
"parent": "1",
"font_face_settings": {
"fontStyle": "normal",
"fontWeight": "400",
"fontFamily": "Piazzolla",
"src": [
"https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.ttf",
"https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.woff2"
],
"preview": "https://my-wp-site.com/wp-content/fonts/previews/piazzolla-400-normal.svg"
},
"_links": {
"self": [{
"href": "https://my-wp-site.com/wp-json/wp/v2/font-families/1/font-faces/1835"
}],
"collection": [{
"href": "https://my-wp-site.com/wp-json/wp/v2/font-families/1/font-faces"
}],
"parent": [{
"href": "https://my-wp-site.com/wp-json/wp/v2/font-families/1"
}]
}
}POST wp/v2/font-families/<id>/font-faces
Add a font face to an existing font family.
Creates one font face for a font family, stores its asset(s), and creates its preview(s). Currently, the font face assets can be added in 2 ways:
- Provide a src that points to a binary file included in the request (multipart/form-data)
- Provide a src property for the font face that points to a (local or remote) URL
The Content-Type is always multipart/form-data with a JSON string for the data, so that file uploads are supported when used.
Upload font face assets attached in the http request:
Request:
POST wp/v2/font-families/1/font-faces
Content-Type: multipart/form-data;
file-0-0 (binary)
file-0-1 (binary)
{
"theme_json_version": 2,
"font_face_settings": {
"fontStyle": "normal",
"fontWeight": "400",
"fontFamily": "Piazzolla",
// ... can include any font face properties in theme.json
"src": [
"file-0-0",
"file-0-1"
]
}
}Response:
{
"id": 1835,
"theme_json_version": 2,
"parent": "1",
"font_face_settings": {
"fontStyle": "normal",
"fontWeight": "400",
"fontFamily": "Piazzolla",
"src": [
"https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.ttf",
"https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.woff2"
],
"preview": ""
}
}Install font definition without processing src:
Request:
POST wp/v2/font-families/1/font-faces
Content-Type: multipart/form-data;
{
"theme_json_version": 2,
"font_face_settings": {
"fontStyle": "normal",
"fontWeight": "400",
"fontFamily": "Piazzolla",
"src": "https://example.com/fonts/piazzolla-400-normal.ttf"
}
}Response:
{
"id": 1835,
"parent": "1",
"theme_json_version": 2,
"font_face_settings": {
"fontStyle": "normal",
"fontWeight": "400",
"fontFamily": "Piazzolla",
"src": "https://example.com/fonts/piazzolla-400-normal.ttf",
"preview": "https://example.com/fonts/piazzolla-400-normal.ttf"
}
}DELETE wp/v2/font-families/<id>/font-faces/<id>
Delete one font face from a font family.
Request:
DELETE wp/v2/font-families/1/font-faces/462?force=true
Response:
{
"deleted": true,
"previous" : {
"id": 462,
"theme_json_version": 2,
"parent": 1,
"font_face_settings": {
"fontStyle": "normal",
"fontWeight": "400",
"fontFamily": "Piazzolla",
"src": "https://my-wp-site.com/wp-content/fonts/piazzolla-400-normal.ttf",
"preview": "https://my-wp-site.com/wp-content/fonts/previews/piazzolla-400-normal.ttf"
}
}
}Font Collections
A font collection is a list of font family definitions that can be installed.
GET wp/v2/font-collections
List font collections
Request:
GET wp/v2/font-collections
Response:
[
{
"slug": "collection-example",
"name": "Collection Example",
"description": "This a Font Collection Example",
"font_families": [
{
"categories": [ "sans-serif" ],
"font_family_settings": {
"name": "Roboto",
"fontFamily": "Roboto, sans-serif",
"slug": "roboto",
"category": "sans-serif",
"fontFace": [
{
"src": "https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgWxPKTM1K9nz.ttf",
"fontWeight": "400",
"fontStyle": "normal",
"fontFamily": "Roboto",
"preview": "https://s.w.org/images/fonts/16.7/previews/roboto/roboto-400-normal.svg"
},
{
"src": "https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrIzcXLsnzjYk.ttf",
"fontWeight": "400",
"fontStyle": "italic",
"fontFamily": "Roboto",
"preview": "https://s.w.org/images/fonts/16.7/previews/roboto/roboto-400-italic.svg"
}
]
}
}
],
"categories": [
{
"slug": "sans-serif",
"name": "Sans Serif"
}
]
},
{
"slug": "default-font-collection",
"name": "Google Fonts",
"description": "Google Fonts collection.",
"font_families": [
// font familiy objects
]
"categories": [
// category objects
]
}
]GET wp/v2/font-collections/
Get font collection by slug.
Request:
GET wp/v2/font-collections/collection-example
Response:
{
"slug": "collection-example",
"name": "Collection Example",
"description": "Example font collection.",
"theme_json_version": 2,
"font_families": [
{
"categories": [ "sans-serif" ],
"font_family_settings": {
"name": "Roboto",
"fontFamily": "Roboto, sans-serif",
"slug": "roboto",
"category": "sans-serif",
"fontFace": [
{
"src": "https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgWxPKTM1K9nz.ttf",
"fontWeight": "400",
"fontStyle": "normal",
"fontFamily": "Roboto",
"preview": "https://s.w.org/images/fonts/16.7/previews/roboto/roboto-400-normal.svg"
},
{
"src": "https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrIzcXLsnzjYk.ttf",
"fontWeight": "400",
"fontStyle": "italic",
"fontFamily": "Roboto",
"preview": "https://s.w.org/images/fonts/16.7/previews/roboto/roboto-400-italic.svg"
},
]
}
}
],
"categories": [
{
"slug": "sans-serif",
"name": "Sans Serif"
}
]
}Activating Font Families and Font Faces
Activating a font family and/or font faces is done by directly updating Global Styles using the existing endpoint (wp/v2/global-styles), since adding the fonts to the Global Styles settings is what adds them as typography options for styles across the site.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fields✅ Done
Activity
matiasbenedetto commentedon Jan 10, 2024
This is the new shape for the font families endpoints proposed.
@TimothyBJacobs @spacedmonkey @swissspidy @costdev @hellofromtonya @kadamwhite It would be important if you could take a look here and share your thoughts about this refactoring proposal.
matiasbenedetto commentedon Jan 11, 2024
I added the Font Collections API specs in the issue description.
creativecoder commentedon Jan 11, 2024
I removed the
POST wp/v2/font-families/<id>/font-faces/<id>endpoint from the design. At least for now, it's clearer to delete an exiting font face and re-create a new one with modified settings, and avoid the complexity of detecting whether to replace src font files, or not, depending on the new settings.creativecoder commentedon Jan 16, 2024
Noting here that we have the following endpoints implemented in a feature branch that can be reviewed directly:
We're still tweaking small bits as we update the client to work with the new API structure, but these should be in near final form.
Here's the feature branch PR: #57688
Feedback now is welcome, as this will be very close to what we plan to propose merging for WP 6.5 (after merging to Gutenberg trunk for 17.6).
creativecoder commentedon Jan 26, 2024
Noting that I've updated the font collection endpoints in the description to reflect that we're returning the full data for each collection in
GET wp/v2/font-collectionsrequests. The response can be customized using the _fields param.costdev commentedon Jan 26, 2024
@creativecoder Thanks for the ping! Just a few questions for clarity come to mind at the moment.
Is there a benefit in having
theme_jsonin this name, as opposed to calling itversion? Is it for clarity, or is it intentional to prevent a foreseen clash in future, or something else?I don't think that I saw any examples where both
font_family_settingsandfont_face_settingswould be on the same level in the same response. Could we just usesettingsfor these since the context is clear from the endpoint and/or thefont_families: { settings: {} }path, or is there a reason to be specific with these names?In the example, the path is
font_families[ 0 ] => { fontFace: [] }- Since it's an array of font faces, should this instead befontFaces?creativecoder commentedon Jan 27, 2024
Thanks @costdev for taking a look!
It's for clarity. Font family and face definitions come directly from the theme.json schema, so this is meant to clarify it's the theme.json version, not the API namespace version, css spec level, or anything else.
That's correct, these 2 properties are not used in the same level of any responses. We could use
settingsin both cases and it should be clear from the placement within the response. This would also be closer to the theme.json schema and Global Styles request/response shape, which starts withsettings:settings: { typography: { fontFamilies: [ { fontFace: [ {} ] } ] } }That would make sense, though the theme.json schema is already using
fontFace, so it's that way here too, for consistency.peterwilsoncc commentedon Jan 30, 2024
My understanding is that this was discussed and architectured on internal A8C systems for Team Pixel. I think this is a good demonstration of why even non-Automatticians felt the editor chats could be canceled.
Further discussions like this are examples of issues that ought to be discussed publicly, either on the make/core P2 or - at the least - in GitHub discussions on this repo with a call to action on make/core.
priethor commentedon Jan 31, 2024
Hi Peter, thanks for the feedback 🙏 . A make/core post for increased awareness could have been more helpful. On the other hand, how would a GitHub discussion make it more public?
peterwilsoncc commentedon Jan 31, 2024
I'm talking exclusively of the architecture discussions that took place on the Team Pixel P2 and in the team's docs in the period prior to this issue being opened. A significant number of contributors do not have access to these discussions so discussing them is this repo or on make/core will open the discussions up to all contributors, not just those sponsored by Automattic.
annezazu commentedon Feb 5, 2024
Since this has been merged #57688 are we able to move this out or through the 6.5 board? It's unclear to me if any discussion will continue here or what the role of this issue is at this point. Happy to keep it open! Just want clarity and for the board to be as up to date as possible.
creativecoder commentedon Feb 5, 2024
Thanks for asking @annezazu ! Personally, I'd like to keep this issue open, at least until after the Core PR for the revised Font Library REST API implementation is opened (should be very soon) and has received feedback.
If there are any revisions needed in the API design based on that feedback, I think it would be good to track that here for continuity.
getdave commentedon Feb 12, 2024
hi @creativecoder Are you happy you've received feedback for the REST API yet? If not please let me know and I will reach out for input. Thank you.
creativecoder commentedon Feb 14, 2024
Now that the API endpoints have been merged to Core, I'll close this issue. We can open separate issues if there are specific things that need to be addressed. Thank you!