Skip to content

Instantly share code, notes, and snippets.

@daiplusplus
Created August 21, 2020 08:10
Show Gist options
  • Save daiplusplus/6f73bf3154adca9c58b636386aff495e to your computer and use it in GitHub Desktop.
Save daiplusplus/6f73bf3154adca9c58b636386aff495e to your computer and use it in GitHub Desktop.
Automatically generate CSS file with data: URIs in custom properties
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".css" #>
<#
// https://css-tricks.com/did-you-know-that-css-custom-properties-can-handle-images-too/
// https://wolfgang-ziegler.com/blog/using-relative-paths-in-t4-templates
DirectoryInfo scriptDirectory = new DirectoryInfo( this.Host.ResolvePath( String.Empty ) );// The "$/WebApp/images" directory.
IReadOnlyDictionary<String,String> includeFileNames = new Dictionary<String,String>() {
{ "metal-v1.png" , "metal" },
{ "logoArrowOne.png" , "logo-arrow" },
{ "Chevron-Left-Black.svg" , "chevron-left-black" },
{ "Chevron-Left-White.svg" , "chevron-left-white" },
{ "Chevron-Right-Black.svg" , "chevron-right-black" },
{ "Chevron-Right-White.svg" , "chevron-right-white" },
// TODO: These radio button images need to be recreated with hand-written SVG to be super optimal for data-URIs - and better visual appearance.
{ "Radio-White-Unchecked.svg" , "radio-unchecked-white" },
{ "Radio-White-Checked.svg" , "radio-checked-white" },
{ "Radio-Black-Unchecked.svg" , "radio-unchecked-black" },
{ "Radio-Black-Checked.svg" , "radio-checked-black" },
{ "Checkbox-Black-Unchecked.svg" , "checkbox-unchecked-black" },
{ "Checkbox-Black-Checked.svg" , "checkbox-checked-black" },
{ "Checkbox-White-Unchecked.svg" , "checkbox-unchecked-white" },
{ "Checkbox-White-Checked.svg" , "checkbox-checked-white" },
{ "NounProject/noun_drag_796138.svg" , "draghandle-black" },
{ "NounProject/noun_drag_796138-white.svg" , "draghandle-white" },
{ "NounProject/noun_CheckMark_835.svg" , "pass" },
{ "NounProject/noun_Warning_488896.svg" , "warn" },
{ "NounProject/noun_fail_334199.svg" , "fail" },
{ "NounProject/noun_Information_488911.svg" , "notes" },
{ "NounProject/noun_exclamation_1081370-red.svg" , "invalid" },
{ "NounProject/Hidden_black.svg" , "hidden" },
{ "NounProject/Required-Red.svg" , "required" },
};
IReadOnlyList<ImageFile> expectedFiles = includeFileNames
.Select( kvp => new
{
RelativePath = kvp.Key,
FriendlyName = kvp.Value,
FullPath = Path.Combine( scriptDirectory.FullName, kvp.Key )
} )
.Select( t => new ImageFile( t.RelativePath, new FileInfo( t.FullPath ), t.FriendlyName ) )
.ToList();
#>
/*
Example usage:
<# foreach( ImageFile imageFile in expectedFiles ) { #>
selector {
background-image: url("/images/<#= imageFile.RelativePath #>");
background-image: var(--image-<#= imageFile.FriendlyName #>);
}
<# } #>
*/
:root {
<# foreach( ImageFile imageFile in expectedFiles ) { #>
/* <#= imageFile.FriendlyName #> :: <#= imageFile.RelativePath #> */
--image-url-<#= imageFile.FriendlyName #>: url("/images/<#= imageFile.RelativePath #>");
--image-<#= imageFile.FriendlyName #> : url("<#= imageFile.Base64DataUri #>");
<# } #>
}
<#+
class ImageFile {
public ImageFile( String relativePath, FileInfo fileInfo, String friendlyName )
{
this.RelativePath = relativePath;
this.FileInfo = fileInfo;
this.FriendlyName = friendlyName;
this.Base64DataUri = CreateDataUri( fileInfo );
}
public String RelativePath { get; }
public FileInfo FileInfo { get; }
public String FriendlyName { get; }
public String Base64DataUri { get; }
}
static String CreateDataUri( FileInfo fi ) {
if( !fi.Exists ) throw new FileNotFoundException( "File \"" + fi.FullName + "\" does not exist." );
String contentType;
switch( fi.Extension )
{
case ".svg":
contentType = "image/svg+xml";
break;
case ".png":
contentType = "image/png";
break;
default:
throw new InvalidOperationException( "Unsupported file extension or MIME type." );
}
//
Byte[] fileData = File.ReadAllBytes( fi.FullName );
String base64 = Convert.ToBase64String( fileData );
String dataUri = "data:" + contentType + ";base64," + base64;
return dataUri;
}
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment