This is a plugin for Remark, and allows you to embed file attachments in markdown files. The plugin offers the ability to style the file attachments, so that they look like more than a simple link, and arguably it offers better accessability as well. This plugin can also be used with Gridsome.
npm i remark-file-attachment --save-dev
# yarn add remark-file-attachment --dev
You can configure the HTML element type to be used for the attachment container and the attachment item.
- Attachment container (default ul)
containerElement: "ul",
- Attachment item (default li)
containerElement: "li",
You can provide css and styles for just about all of the attachment's HTML elements.
-
Attachment container (default ul)
containerCss: "border border-gray-200 rounded-md divide-y divide-gray-200 max-w-lg mb-3", containerStyle: "list-style: none !important; margin-top: 0 !important; margin-bottom: 0 !important;",
-
Attachment item (default li)
itemCss: "pr-4 py-3 flex items-center justify-between text-sm text-gray-400 focus-within:text-blue-600", itemStyle: "content: none; position: static !important; margin-top: 0 !important; margin-bottom: 0 !important; padding-left: 0.75rem !important;",
-
Icon and paragraph container (div)
iconAndParagraphContainerCss: "flex items-center max-w-32 md:max-w-max pr-4",
-
Paragraph (span)
paragraphCss: "ml-2 truncate text-gray-800 max-w-xs",
-
Link container (div)
linkContainerCss: "ml-3 inline-flex space-x-4",
-
Show link (a)
showLinkCss: "font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline", showLinkStyle: "text-decoration: none;",
-
Download link (a)
downloadLinkCss: "font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline", downloadLinkStyle: "text-decoration: none;",
You can provide HTML for an icon next to the attachment name (paragraph).
Example:
iconHTML: `
<svg focusable="false" aria-hidden="true" class="flex-shrink-0 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8 4a3 3 0 00-3 3v4a5 5 0 0010 0V7a1 1 0 112 0v4a7 7 0 11-14 0V7a5 5 0 0110 0v4a3 3 0 11-6 0V7a1 1 0 012 0v4a1 1 0 102 0V7a3 3 0 00-3-3z" clip-rule="evenodd" />
</svg>
`,
If you want alignment to work, you'll have to supply css classes.
There's three options:
-
cssClassToCenterPlaceholder
Example value
w-full flex justify-center
-
cssClassToLeftAlignPlaceholder
Example value
w-full flex justify-start
-
cssClassToRightAlignPlaceholder
Example value
w-full flex justify-end
Note: You can also add extra css, like bottom margin, to space things out nicely.
You can provide text to use for the "Show" and "Download" link, so thay they fit with the locale of the webiste.
showLinkText: "Show",
showLinkAltSuffix: "in new tab",
// The alt text of the show link will be showLinkText + " " + attachment text attribute + showLinkAltSuffix.
// Show Document 1.pdf in new tab
downloadLinkText: "Download",
// The alt text of the download link will be downloadLinkText + " " + attachment text attribute.
// Download Document 1.pdf
const remark = require("remark");
const fileAttachment = require("remark-file-attachment");
const processor = remark().use(fileAttachment, {
cssClassToCenter: "w-full flex justify-center",
cssClassToLeftAlign: "w-full flex justify-start",
cssClassToRightAlign: "w-full flex justify-end",
containerElement: "ul",
containerCss: "border border-gray-200 rounded-md divide-y divide-gray-200 max-w-lg mb-3",
containerStyle: "list-style: none !important; margin-top: 0 !important; margin-bottom: 0 !important;",
itemElement: "li",
itemCss: "pr-4 py-3 flex items-center justify-between text-sm text-gray-400 focus-within:text-blue-600",
itemStyle: "content: none; position: static !important; margin-top: 0 !important; margin-bottom: 0 !important; padding-left: 0.75rem !important;",
iconHTML: `
<svg focusable="false" aria-hidden="true" class="flex-shrink-0 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8 4a3 3 0 00-3 3v4a5 5 0 0010 0V7a1 1 0 112 0v4a7 7 0 11-14 0V7a5 5 0 0110 0v4a3 3 0 11-6 0V7a1 1 0 012 0v4a1 1 0 102 0V7a3 3 0 00-3-3z" clip-rule="evenodd" />
</svg>
`,
paragraphCss: "ml-2 truncate text-gray-800 max-w-xs",
iconAndParagraphContainerCss: "flex items-center max-w-32 md:max-w-max pr-4",
linkContainerCss: "ml-3 inline-flex space-x-4",
showLinkText: "Show",
showLinkAltTextSuffix: "in new tab",
showLinkCss: "font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline",
downloadLinkText: "Download",
downloadLinkCss: "font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline",
downloadLinkStyle: "text-decoration: none;",
});
module.exports = {
plugins: [
{
use: "@gridsome/source-filesystem",
options: {
path: "blog/**/*.md",
route: "/blog/:year/:month/:day/:slug",
remark: {
plugins: [
[
"remark-file-attachment",
{
cssClassToCenter: "w-full flex justify-center",
cssClassToLeftAlign: "w-full flex justify-start",
cssClassToRightAlign: "w-full flex justify-end",
containerElement: "ul",
containerCss: "border border-gray-200 rounded-md divide-y divide-gray-200 max-w-lg mb-3",
containerStyle: "list-style: none !important; margin-top: 0 !important; margin-bottom: 0 !important;",
itemElement: "li",
itemCss: "pr-4 py-3 flex items-center justify-between text-sm text-gray-400 focus-within:text-blue-600",
itemStyle: "content: none; position: static !important; margin-top: 0 !important; margin-bottom: 0 !important; padding-left: 0.75rem !important;",
iconHTML: `
<svg focusable="false" aria-hidden="true" class="flex-shrink-0 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8 4a3 3 0 00-3 3v4a5 5 0 0010 0V7a1 1 0 112 0v4a7 7 0 11-14 0V7a5 5 0 0110 0v4a3 3 0 11-6 0V7a1 1 0 012 0v4a1 1 0 102 0V7a3 3 0 00-3-3z" clip-rule="evenodd" />
</svg>
`,
paragraphCss: "ml-2 truncate text-gray-800 max-w-xs",
iconAndParagraphContainerCss: "flex items-center max-w-32 md:max-w-max pr-4",
linkContainerCss: "ml-3 inline-flex space-x-4",
showLinkText: "Show",
showLinkAltTextSuffix: "in new tab",
showLinkCss: "font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline",
downloadLinkText: "Download",
downloadLinkCss: "font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline",
downloadLinkStyle: "text-decoration: none;",
},
]
]
}
}
}
]
}
The markdown must consist of all the possible attributes (attachment, text & placement), and you must enclose the markdown in backticks (`).
Format:
`attachment [FILE_LINK] text [TEXT] placement [Left|Center|Right]`
Examples:
`attachment /uploads/salesoverview.pdf text Sales overview report placement Left end`
or
`attachment /uploads/salesoverview.pdf text Sales overview report placement Left end attachment /uploads/purchaseoverview.pdf text Purchase overview report placement Left end`
This is how the attachment/s can appear on the screen:
<div class="w-full flex justify-start">
<ul class="border border-gray-200 rounded-md divide-y divide-gray-200 max-w-lg mb-3" style="list-style: none !important; margin-top: 0 !important; margin-bottom: 0 !important;">
<li class="pr-4 py-3 flex items-center justify-between text-sm text-gray-400 focus-within:text-blue-600" style="content: none; position: static !important; margin-top: 0 !important; margin-bottom: 0 !important; padding-left: 0.75rem !important;">
<div class="flex items-center max-w-32 md:max-w-max pr-4" style="">
<svg focusable="false" aria-hidden="true" class="flex-shrink-0 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8 4a3 3 0 00-3 3v4a5 5 0 0010 0V7a1 1 0 112 0v4a7 7 0 11-14 0V7a5 5 0 0110 0v4a3 3 0 11-6 0V7a1 1 0 012 0v4a1 1 0 102 0V7a3 3 0 00-3-3z" clip-rule="evenodd"></path></svg>
<span class="ml-2 truncate text-gray-800 max-w-xs" style="">Sales overview report</span>
</div>
<div class="ml-3 inline-flex space-x-4" style="">
<a href="/uploads/salesoverview-report.pdf" target="_blank" rel="noopener noreferrer" aria-label="Show attachment in new tab" class="font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline">Show</a>
<a href="/uploads/salesoverview-report.pdf" download="" aria-label="Download attachment" class="font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline">Download</a>
</div>
</li>
<li class="pr-4 py-3 flex items-center justify-between text-sm text-gray-400 focus-within:text-blue-600" style="content: none; position: static !important; margin-top: 0 !important; margin-bottom: 0 !important; padding-left: 0.75rem !important;">
<div class="flex items-center max-w-32 md:max-w-max pr-4" style="">
<svg focusable="false" aria-hidden="true" class="flex-shrink-0 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M8 4a3 3 0 00-3 3v4a5 5 0 0010 0V7a1 1 0 112 0v4a7 7 0 11-14 0V7a5 5 0 0110 0v4a3 3 0 11-6 0V7a1 1 0 012 0v4a1 1 0 102 0V7a3 3 0 00-3-3z" clip-rule="evenodd"></path></svg>
<span class="ml-2 truncate text-gray-800 max-w-xs" style="" title="Purchase overview report">Purchase overview report</span>
</div>
<div class="ml-3 inline-flex space-x-4" style="">
<a href="/uploads/purchaseoverview-report.pdf" target="_blank" rel="noopener noreferrer" aria-label="Show Purchase overview report in new tab" class="font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline">Show</a>
<a href="/uploads/purchaseoverview-report.pdf" download="" aria-label="Download Purchase overview report" class="font-medium text-blue-600 hover:text-blue-500 outline-none focus:underline">Download</a>
</div>
</li>
</ul>
</div>
When the plugin detects errors, i.e. an incorrectly formatted attachment element, it will render a red fat error instead of the attachment.
remark-file-attachment Error: The markdown is not correctly formatted.
MIT