diff --git a/.eslintrc.yml b/.eslintrc.yml index 5beac00365..6611c1e452 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,11 +1,34 @@ env: browser: true es2021: true + node: true extends: - airbnb-base - plugin:prettier/recommended -overrides: [] +parser: '@typescript-eslint/parser' parserOptions: ecmaVersion: latest -rules: { "prettier/prettier": "error" } -plugins: ["prettier"] + sourceType: module +plugins: + - '@typescript-eslint' + - prettier +rules: + prettier/prettier: error +settings: + import/resolver: + node: + extensions: ['.js', '.jsx', '.ts', '.tsx'] +overrides: + - files: ['*.js', '*.jsx'] + rules: + prettier/prettier: error + - files: ['*.ts', '*.tsx'] + extends: + - plugin:@typescript-eslint/recommended + rules: + prettier/prettier: error + no-console: off # This disables the console statement warning for TypeScript files + consistent-return: off # This disables the consistent return warning for TypeScript files + '@typescript-eslint/no-explicit-any': off # This disables the explicit any warning for TypeScript files + 'no-await-in-loop': off # This disables the await in loop warning for TypeScript files + 'no-restricted-syntax': off # This disables the restricted syntax warning for TypeScript files \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 88d87c36fe..edf5d70764 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,8 +1,8 @@ ## Summary -Basic description of work done. Closes [#issue_no]. +Basic description of work done. -### Preview +## Preview [Link to Preview]() @@ -10,18 +10,43 @@ Basic description of work done. Closes [#issue_no]. ⚠️ Significant visual changes require submitting previous design to wayback machine. --> -### Solution +## Solution -### How To Test +Provide a summary of the solution this PR offers. + + + + +## How To Test 1. First Step 2. Second Step 3. Third Step ---- + + + + diff --git a/.gitignore b/.gitignore index b49998555b..dc15178f22 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ content/images/_inbox/Refine_Listen.png content/images/_inbox/Triangulate.png static/img/proxy/fedramp-logo-stamp.jpeg .hugo_build.lock +.vscode/launch.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..3ada9b2d49 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "always" + } + } \ No newline at end of file diff --git a/config/gulp/file-prep.js b/config/gulp/file-prep.js index 2c241995d7..e1d850df86 100644 --- a/config/gulp/file-prep.js +++ b/config/gulp/file-prep.js @@ -22,12 +22,11 @@ const allExtensions = [...imageExtensions, ...fileExtensions]; // removes . separator to read jpg,png,jpeg... const extensionsString = allExtensions .map((extension) => extension.replace(".", "")) - .join(","); + .join(","); const imageRegex = /(png|jpg|jpeg)/; const fileRegex = /(doc|docx|pdf|ppt|pptx|pptm|xls|xlsx)/; - /** * Converts JPG images to PNG format * @param {string} imagePath - path of the image file @@ -36,9 +35,7 @@ async function convertJpgToPng(imagePath) { console.log(`Converting image ${imagePath} to PNG`); const outputPath = imagePath.replace(/\.jpe?g$/i, ".png"); - await sharp(imagePath) - .toFormat("png") - .toFile(outputPath); + await sharp(imagePath).toFormat("png").toFile(outputPath); // Check if the original JPG file exists before unlinking if (fs.existsSync(imagePath)) { @@ -48,8 +45,6 @@ async function convertJpgToPng(imagePath) { return path.basename(outputPath); } - - /** * Object containing working folder paths used for lifecycle steps of uploading * to-process contains the normalized filename, static files are upload to s3 from here @@ -67,7 +62,6 @@ const filePaths = { }, }; - /** * Creates directories for each step of the file uploading process * These directories are removed when a file has been uploaded @@ -80,7 +74,9 @@ function fileTidy(done) { fs.readdir(paths.uploads, async (err, files) => { if (err) { - console.error(`Failed to read directory ${paths.uploads}: ${err.message}`); + console.error( + `Failed to read directory ${paths.uploads}: ${err.message}` + ); done(err); return; } @@ -93,29 +89,44 @@ function fileTidy(done) { // create working directories if they do not exist createDir(paths[filetype].toProcess, 3); if (filetype === "image") createDir(paths[filetype].processed, 3); - // copies uploaded file to /to-process with new normalized name + // copies uploaded file to /to-process with new normalized name // convert jpg to png - if (filetype === "image" && fileExt === ".jpg" || fileExt === ".jpeg") { - let convertedPng = await convertJpgToPng(path.join(paths.uploads, file)).catch - ((err) => { - console.error(`Error converting image ${file} to PNG: ${err.message}`); + if ( + (filetype === "image" && fileExt === ".jpg") || + fileExt === ".jpeg" + ) { + let convertedPng = await convertJpgToPng( + path.join(paths.uploads, file) + ).catch((err) => { + console.error( + `Error converting image ${file} to PNG: ${err.message}` + ); return; }); - // ensure the folder for the process image exists. + // ensure the folder for the process image exists. if (convertedPng != file) { file = convertedPng; } - - } + } newfileName = cleanFileName(file); const newFilePath = path.join(dirToProcess, newfileName); // Rename and move the file to the new path try { - console.log(`Moving file from ${path.join(paths.uploads, file)} to ${newFilePath}`); - fs.renameSync(path.join(paths.uploads, file), newFilePath); + console.log( + `Moving file from ${path.join( + paths.uploads, + file + )} to ${newFilePath}` + ); + fs.renameSync(path.join(paths.uploads, file), newFilePath); } catch (renameError) { - console.error(`Error moving file from ${path.join(paths.uploads, file)} to ${newFilePath}: ${renameError.message}`); + console.error( + `Error moving file from ${path.join( + paths.uploads, + file + )} to ${newFilePath}: ${renameError.message}` + ); continue; } } @@ -124,8 +135,6 @@ function fileTidy(done) { }); } - - /** * creates the originals and to-process directories for both files and images * ./content/uploads/_working-images/to-process"; @@ -178,7 +187,7 @@ function fileType(extension) { * @returns filename in string format */ function cleanFileName(origfilename) { - return origfilename + return origfilename .toLowerCase() .replace(/[ &$_#!?.]/g, "-") .replace(/-+/g, "-") // multiple dashes to a single dash @@ -188,7 +197,7 @@ function cleanFileName(origfilename) { .replace(/^\d{2,4}-*x-*\d{2,4}-*/g, "") // strip leading dimensions .replace(/-\./g, ".") // remove leading dashes .replace(/^-/g, "") // removes dashes from start of filename - .toLowerCase(); + .toLowerCase(); } /** diff --git a/config/gulp/file-process.js b/config/gulp/file-process.js index b327a4a592..3be32dfc25 100644 --- a/config/gulp/file-process.js +++ b/config/gulp/file-process.js @@ -122,7 +122,7 @@ async function processImageVariants(image) { async function processImages() { return new Promise((resolve, reject) => { fs.readdir(`${processImagesDirectory}`, async (err, images) => { - if (images === undefined) { + if (images === undefined) { resolve(); return; } @@ -155,15 +155,15 @@ async function processImages() { * Removes the /to-process temporary working folder after variants are created */ function removeProcessedImage() { -return new Promise((resolve, reject) => { - const imageDir = "content/uploads/_working-images/processed"; - - if (fs.existsSync(imageDir) && fs.readdirSync(imageDir).length > 0) { - return del([ - "content/uploads/_working-images/to-process", - ]).then(() => resolve()).catch((err) => reject(err)); - } else { - resolve(); + return new Promise((resolve, reject) => { + const imageDir = "content/uploads/_working-images/processed"; + + if (fs.existsSync(imageDir) && fs.readdirSync(imageDir).length > 0) { + return del([ + "content/uploads/_working-images/to-process", + ]).then(() => resolve()).catch((err) => reject(err)); + } else { + resolve(); } }); } diff --git a/config/gulp/file-upload.js b/config/gulp/file-upload.js deleted file mode 100644 index 41ded5d44b..0000000000 --- a/config/gulp/file-upload.js +++ /dev/null @@ -1,152 +0,0 @@ -require("dotenv").config(); - -const gulp = require("gulp"); -const awspublish = require("gulp-awspublish"); -const rename = require("gulp-rename"); -const del = require("del"); -const vinylPaths = require("vinyl-paths"); -const fs = require("fs"); -const { resolve } = require("path"); -// Create a new publisher using S3 options -const publisher = awspublish.create({ - region: "us-east-1", // Change to your AWS region - params: { - Bucket: "digitalgov", // Change to your bucket name - }, - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, -}); - -const headers = { - "Cache-Control": "max-age=315360000, no-transform, public", - 'x-amz-acl': 'public-read' -} - -function uploadImage() { - return gulp - .src("content/uploads/_working-images/processed/*") - .pipe(publisher.publish(headers)) - .pipe( - awspublish.reporter({ - states: ["create", "update", "delete"], - }) - ) - .pipe(vinylPaths(del)) - .on("error", function (err) { - console.error("Image upload failed:", err); - }); -} - -function uploadFile() { - // renames file to /static/filename to upload to digitalgov/static directory - const staticRename = rename((path) => { - path.basename = `/static/${path.basename}`; - }); - - return gulp - .src("content/uploads/_working-files/to-process/*") - .pipe(staticRename) - .pipe(publisher.publish(headers)) - .pipe( - awspublish.reporter({ - states: ["create", "update", "delete"], - }) - ) - .pipe(vinylPaths(del)) - .on("error", function (err) { - console.error("File upload failed:", err); - }); -} - -function cleanup() { - return new Promise((resolve, reject) => { - let imageDir = "content/uploads/_working-images/processed"; - let fileDir = "content/uploads/_working-files/to-process"; - - if (fs.existsSync(imageDir)) { - if (fs.readdirSync(imageDir).length > 0) { - console.log(`Images have number of files ${fs.readdirSync(imageDir).length}`); - // delete tht folder - del([imageDir]); - resolve(); - } else { - resolve(); - } - } else { - resolve(); - } - }); -} - -/** - * Determines which files to upload and initiates the upload process. - * @param {Function} done - A callback function to signal that the task is complete. - * - * Key Points: - * 1. The function checks if there are images and files to upload. - * 2. If there are images, it initiates the upload process. - * 3. If there are files, it initiates the upload process. - * 4. The function keeps track of the number of uploads that need to be completed. - * 5. After all uploads are complete, the `done` callback is called to signal the task is complete. - */ -function determineWhichToUpload() { - return new Promise((resolve, reject) => { - const imageDir = "content/uploads/_working-images/processed/"; - const fileDir = "content/uploads/_working-files/to-process/"; - let imageDirExists = fs.existsSync(imageDir); - let fileDirExists = fs.existsSync(fileDir); - let imageFiles = imageDirExists ? fs.readdirSync(imageDir) : []; - let fileFiles = fileDirExists ? fs.readdirSync(fileDir) : []; - - if (!imageDirExists && !fileDirExists) { - console.log("No files or images to upload."); - return resolve(); - } - - let uploadsToComplete = 0; - let uploadsCompleted = 0; - - const checkCompletion = () => { - if (uploadsCompleted === uploadsToComplete) { - resolve(); - } - }; - - if (imageFiles.length > 0) { - uploadsToComplete += 1; - const imageUploadStream = uploadImage(); - imageUploadStream.on('finish', () => { - uploadsCompleted += 1; - checkCompletion(); - }); - imageUploadStream.on('error', (err) => { - console.error("Error uploading images:", err); - reject(err); - }); - } - - if (fileFiles.length > 0) { - uploadsToComplete += 1; - const fileUploadStream = uploadFile(); - fileUploadStream.on('finish', () => { - uploadsCompleted += 1; - checkCompletion(); - }); - fileUploadStream.on('error', (err) => { - console.error("Error uploading files:", err); - reject(err); - }); - } - - if (uploadsToComplete === 0) { - resolve(); // If no uploads are initiated, resolve immediately. - } - }); -} - - - -/** - * Exports a Gulp task series that first determines which files to upload and then cleans up. - */ -exports.do = gulp.series(determineWhichToUpload, cleanup); \ No newline at end of file diff --git a/config/gulp/readme.md b/config/gulp/readme.md index 316ed96f79..5e554b8e88 100644 --- a/config/gulp/readme.md +++ b/config/gulp/readme.md @@ -10,7 +10,20 @@ The image and file upload processing feature consists of the following scripts: 2. `file-process.js`: Processes the uploaded images by creating responsive variants and compressing the original image. -3. `file-upload.js`: Uploads the processed images and files to Amazon S3 and cleans up temporary directories. +3. `upload.js`: Uploads the processed images and files to Amazon S3 and cleans up temporary directories. +> [!NOTE] +> This script is derived from typescript code located in ./config/typescript/upload.ts. + +If you want to make edits to this code you must recompile the javascript for upload.js, run the following command from the root directory. + +```shell +npm i -g typescript +``` + +Then run this: +``` +tsc +``` 4. `scripts.js`: Bundles and compiles JavaScript files using webpack. diff --git a/config/gulp/upload.js b/config/gulp/upload.js new file mode 100644 index 0000000000..3d7b77efc6 --- /dev/null +++ b/config/gulp/upload.js @@ -0,0 +1,152 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +const AWS = __importStar(require("@aws-sdk/client-s3")); +const dotenv_1 = require("dotenv"); +const fs = __importStar(require("fs")); +const path = require("path"); +const promises_1 = require("fs/promises"); +const mimeTypes = __importStar(require("mime-types")); +(0, dotenv_1.config)(); +const client = new AWS.S3Client({ + region: "us-east-1", + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, +}); +const bucketName = "digitalgov"; +const imageDir = "content/uploads/_working-images/processed/"; +const fileDir = "content/uploads/_working-files/to-process/"; +/** + * Uploads an image to s3 bucket + * @param {string} filePath - path of the file + * @param {string} fileName - name of the file + */ +async function uploadImage(filePath, fileName) { + try { + const fileContent = await fs.promises.readFile(filePath); + const contentType = mimeTypes.lookup(filePath) || "application/octet-stream"; + await client.send(new AWS.PutObjectCommand({ + Bucket: bucketName, + Key: fileName, + Body: fileContent, + ContentType: contentType, + ACL: "public-read", + })); + const url = `https://${bucketName}.s3.amazonaws.com/${fileName}`; + console.log("Image uploaded successfully. Public URL:", url); + await fs.promises.unlink(filePath); + return url; + } + catch (error) { + console.error("Error uploading image", error); + if (error instanceof Error) { + console.error("Error message:", error.message); + } + if ("$metadata" in error) { + console.error("Error metadata:", error.$metadata); + } + } +} +/** + * Uploads a file to s3 bucket + * @param {string} filePath - path of the file + * @param {string} fileName - name of the file + */ +async function uploadFile(filePath, fileName) { + try { + const fileContent = await fs.promises.readFile(filePath); + await client.send(new AWS.PutObjectCommand({ + Bucket: bucketName, + Key: `static/${fileName}`, + Body: fileContent, + ContentType: "application/octet-stream", + ACL: "public-read", + })); + const url = `https://${bucketName}.s3.amazonaws.com/static/${fileName}`; + console.log("File uploaded successfully. Public URL:", url); + await fs.promises.unlink(filePath); + return url; + } + catch (error) { + console.error("Error uploading file", error); + if (error instanceof Error) { + console.error("Error message:", error.message); + } + if ("$metadata" in error) { + console.error("Error metadata:", error.$metadata); + } + } +} +/** + * Removes the image and file working directories + */ +async function cleanup() { + const imageWorkingDir = "content/uploads/_working-images"; + const fileWorkingDir = "content/uploads/_working-files"; + const deletions = []; + if (fs.existsSync(imageWorkingDir)) { + deletions.push((0, promises_1.rm)(imageWorkingDir, { recursive: true, force: true })); + } + if (fs.existsSync(fileWorkingDir)) { + deletions.push((0, promises_1.rm)(fileWorkingDir, { recursive: true, force: true })); + } + try { + await Promise.all(deletions); + console.log("Cleanup completed"); + } + catch (error) { + console.error("Cleanup failed:", error); + throw error; + } +} +/** + * Initiates the upload process for both images and files + */ +const upload = async () => { + const imageDirExists = fs.existsSync(imageDir); + const imageFiles = imageDirExists ? fs.readdirSync(imageDir) : []; + const fileDirExists = fs.existsSync(fileDir); + const fileFiles = fileDirExists ? fs.readdirSync(fileDir) : []; + try { + // Upload image files + for (const file of imageFiles) { + const filePath = path.join(imageDir, file); + await uploadImage(filePath, file); + } + // Upload static files + for (const file of fileFiles) { + const filePath = path.join(fileDir, file); + await uploadFile(filePath, file); + } + console.log("All uploads completed successfully."); + // Run cleanup after uploads + await cleanup(); + } + catch (error) { + console.error("Upload process failed:", error); + } +}; +module.exports = upload; diff --git a/config/typescript/upload.ts b/config/typescript/upload.ts new file mode 100644 index 0000000000..a31793e3ca --- /dev/null +++ b/config/typescript/upload.ts @@ -0,0 +1,157 @@ +import * as AWS from "@aws-sdk/client-s3"; +import { config } from "dotenv"; +import * as fs from "fs"; +import path = require("path"); +import { rm } from "fs/promises"; +import * as mimeTypes from "mime-types"; + +config(); + +const client = new AWS.S3Client({ + region: "us-east-1", + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID!, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, + }, +}); + +const bucketName = "digitalgov"; +const imageDir = "content/uploads/_working-images/processed/"; +const fileDir = "content/uploads/_working-files/to-process/"; + +/** + * Uploads an image to s3 bucket + * @param {string} filePath - path of the file + * @param {string} fileName - name of the file + */ +async function uploadImage( + filePath: string, + fileName: string +): Promise { + try { + const fileContent = await fs.promises.readFile(filePath); + const contentType = + mimeTypes.lookup(filePath) || "application/octet-stream"; + await client.send( + new AWS.PutObjectCommand({ + Bucket: bucketName, + Key: fileName, + Body: fileContent, + ContentType: contentType, + ACL: "public-read", + }) + ); + + const url = `https://${bucketName}.s3.amazonaws.com/${fileName}`; + console.log("Image uploaded successfully. Public URL:", url); + + await fs.promises.unlink(filePath); + return url; + } catch (error: Error | any) { + console.error("Error uploading image", error); + if (error instanceof Error) { + console.error("Error message:", error.message); + } + if ("$metadata" in error) { + console.error("Error metadata:", (error as any).$metadata); + } + } +} + +/** + * Uploads a file to s3 bucket + * @param {string} filePath - path of the file + * @param {string} fileName - name of the file + */ +async function uploadFile( + filePath: string, + fileName: string +): Promise { + try { + const fileContent = await fs.promises.readFile(filePath); + + await client.send( + new AWS.PutObjectCommand({ + Bucket: bucketName, + Key: `static/${fileName}`, + Body: fileContent, + ContentType: "application/octet-stream", + ACL: "public-read", + }) + ); + + const url = `https://${bucketName}.s3.amazonaws.com/static/${fileName}`; + console.log("File uploaded successfully. Public URL:", url); + + await fs.promises.unlink(filePath); + return url; + } catch (error: Error | any) { + console.error("Error uploading file", error); + if (error instanceof Error) { + console.error("Error message:", error.message); + } + if ("$metadata" in error) { + console.error("Error metadata:", (error as any).$metadata); + } + } +} + +/** + * Removes the image and file working directories + */ +async function cleanup(): Promise { + const imageWorkingDir = "content/uploads/_working-images"; + const fileWorkingDir = "content/uploads/_working-files"; + + const deletions = []; + + if (fs.existsSync(imageWorkingDir)) { + deletions.push(rm(imageWorkingDir, { recursive: true, force: true })); + } + + if (fs.existsSync(fileWorkingDir)) { + deletions.push(rm(fileWorkingDir, { recursive: true, force: true })); + } + + try { + await Promise.all(deletions); + console.log("Cleanup completed"); + } catch (error) { + console.error("Cleanup failed:", error); + throw error; + } +} + +/** + * Initiates the upload process for both images and files + */ +const upload = async (): Promise => { + const imageDirExists = fs.existsSync(imageDir); + const imageFiles = imageDirExists ? fs.readdirSync(imageDir) : []; + + const fileDirExists = fs.existsSync(fileDir); + const fileFiles = fileDirExists ? fs.readdirSync(fileDir) : []; + + try { + // Upload image files + for (const file of imageFiles) { + const filePath = path.join(imageDir, file); + await uploadImage(filePath, file); + } + + // Upload static files + for (const file of fileFiles) { + const filePath = path.join(fileDir, file); + await uploadFile(filePath, file); + } + + console.log("All uploads completed successfully."); + + // Run cleanup after uploads + await cleanup(); + } catch (error) { + console.error("Upload process failed:", error); + } +}; + +export = upload; diff --git a/content/about/contact.md b/content/about/contact.md index d82ca4b328..4ba59d12b2 100644 --- a/content/about/contact.md +++ b/content/about/contact.md @@ -16,7 +16,7 @@ For a wider list of contacts, see our [**Directory of services, tools, and teams ## On social media -- Follow [@digital_gov on Twitter](https://twitter.com/digital_gov/) +- Follow [@digital_gov on X (Formerly Twitter)](https://X.com/digital_gov) - Digital.gov on [GitHub](https://github.com/GSA/digitalgov.gov) - Join our [Facebook page](https://www.facebook.com/DigitalGov) - Subscribe to our [newsletter]({{< ref "/about/subscribe.md" >}}) diff --git a/content/authors/ambuj-neupane/_index.md b/content/authors/ambuj-neupane/_index.md new file mode 100644 index 0000000000..01edbb41a5 --- /dev/null +++ b/content/authors/ambuj-neupane/_index.md @@ -0,0 +1,10 @@ +--- +display_name: Ambuj Neupane +first_name: Ambuj +last_name: Neupane +# e.g. U.S. General Services Administration +agency_full_name: U.S. General Services Administration +# Agency Acronym [e.g., GSA] +agency: GSA +slug: ambuj-neupane +--- diff --git a/content/authors/emily-mcdonald/_index.md b/content/authors/emily-mcdonald/_index.md new file mode 100644 index 0000000000..eb68f7a5ce --- /dev/null +++ b/content/authors/emily-mcdonald/_index.md @@ -0,0 +1,10 @@ +--- +display_name: Emily McDonald +first_name: Emily +last_name: McDonald +# e.g. U.S. General Services Administration +agency_full_name: U.S. Food and Drug Administration +# Agency Acronym [e.g., GSA] +agency: FDA +slug: emily-mcdonald +--- diff --git a/content/authors/merrybelle-guo/_index.md b/content/authors/merrybelle-guo/_index.md new file mode 100644 index 0000000000..326a656c98 --- /dev/null +++ b/content/authors/merrybelle-guo/_index.md @@ -0,0 +1,10 @@ +--- +display_name: Merrybelle Guo +first_name: Merrybelle +last_name: Guo +# e.g. U.S. General Services Administration +agency_full_name: U.S. Food and Drug Administration +# Agency Acronym [e.g., GSA] +agency: FDA +slug: merrybelle-guo +--- diff --git a/content/authors/michael-marin/_index.md b/content/authors/michael-marin/_index.md index a0bf90ebfe..2543c8604f 100644 --- a/content/authors/michael-marin/_index.md +++ b/content/authors/michael-marin/_index.md @@ -2,21 +2,10 @@ display_name: Michael Marin first_name: Michael last_name: Marin -# List your pronoun(s) if you want them displayed alongside your name. -# If blank, we'll use just your name. Learn more http://mypronouns.org -pronoun: "" -# Keep it under 50 words and only one paragraph -bio: Michael Marin is an Information Security Specialist with the United States - Environmental Protection Agency. His professional and research interests - include critical infrastructure security, cyber foreign policy, cyber - regulatory authority, and operational security. Before the EPA, Michael - developed cyber-kinetic training exercises for the U.S. military. Michael has - a bachelor’s and master’s degree in Information Science from the University of - Pittsburgh. # e.g. U.S. General Services Administration -agency_full_name: U.S. Environmental Protection Agency +agency_full_name: National Aeronautics and Space Administration # Agency Acronym [e.g., GSA] -agency: EPA +agency: NASA # [e.g. 'jeremyzilar'] — A GitHub account will allow you to edit pages on Digital.gov. # Also, the image used in your GitHub account can be used to populate your digital.gov profile photo. # Learn more about getting a Github account at [URL] @@ -24,4 +13,5 @@ github: Michael-10101 slug: michael-marin # See [URL] for a full list of profile photo options profile_photo: github + --- diff --git a/content/authors/rachel-mundstock/_index.md b/content/authors/rachel-mundstock/_index.md index 9491c7bd3f..a8baf34539 100644 --- a/content/authors/rachel-mundstock/_index.md +++ b/content/authors/rachel-mundstock/_index.md @@ -20,7 +20,7 @@ slug: rachel-mundstock email: "rachel.mundstock@gsa.gov" # Bio — keep it under 50 words -bio: "Rachel is the Information Specialist for GSA's DigitalGov Search. She helps power the search experience on over 1,500 government websites. Feel free to email her at search@support.digitalgov.gov with any questions or check out DigitalGov Search for more information." +bio: "" # Where can people learn more about your agency or program? Provide a full URL [e.g. 'https://www.example.gov/'] bio_url: "" diff --git a/content/authors/shonna-evans-thompson b/content/authors/shonna-evans-thompson deleted file mode 100644 index 8b13789179..0000000000 --- a/content/authors/shonna-evans-thompson +++ /dev/null @@ -1 +0,0 @@ - diff --git a/content/communities/communicators.md b/content/communities/communicators.md index 8cc6226eb1..9b884dd706 100644 --- a/content/communities/communicators.md +++ b/content/communities/communicators.md @@ -28,7 +28,7 @@ community_list: subscribe_email: "fcn-request@listserv.gsa.gov" subscribe_email_subject: "Join the Communicators Community" terms: "Government employees and contractors with an official .gov or .mil email are eligible to join." - members: 2,453 + members: 2,491 join_cop_button: "Communicators community members" # Controls how this page appears across the site diff --git a/content/communities/contact-center.md b/content/communities/contact-center.md index ea0f728e40..9d1294cad3 100644 --- a/content/communities/contact-center.md +++ b/content/communities/contact-center.md @@ -23,7 +23,7 @@ community_list: subscribe_email: "g3c-request@listserv.gsa.gov" subscribe_email_subject: "Join the Contact center community" terms: "Government employees and contractors with an official .gov or .mil email are eligible to join." - members: 1,000 + members: 373 join_cop_button: "Contact center community members" # Controls how this page appears across the site diff --git a/content/communities/multilingual.md b/content/communities/multilingual.md index 075b341adc..3650cd8032 100644 --- a/content/communities/multilingual.md +++ b/content/communities/multilingual.md @@ -42,7 +42,7 @@ community_list: subscribe_email: "fmwc-request@listserv.gsa.gov" subscribe_email_subject: "Join the Multilingual Community" terms: "Government employees and contractors with an official .gov or .mil email are eligible to join." - members: 738 + members: 739 join_cop_button: "Multilingual community members" kicker: "Join the Multilingual Community" diff --git a/content/communities/plain-language-community-of-practice.md b/content/communities/plain-language-community-of-practice.md index 0ed45c9c79..c1c39ea7ca 100644 --- a/content/communities/plain-language-community-of-practice.md +++ b/content/communities/plain-language-community-of-practice.md @@ -45,7 +45,7 @@ community_list: subscribe_email: "pl-cop-main-request@listserv.gsa.gov" subscribe_email_subject: "Join the Plain Language Community" terms: "Government employees and contractors with an official .gov or .mil email are eligible to join." - members: 2,346 + members: 2,366 join_cop_button: "Plain Language community members" kicker: "Join the Plain Language Community" diff --git a/content/communities/social-media.md b/content/communities/social-media.md index 4573a587a3..a0f9dc68b1 100644 --- a/content/communities/social-media.md +++ b/content/communities/social-media.md @@ -36,7 +36,7 @@ community_list: subscribe_email: "sm-cop-request@listserv.gsa.gov" subscribe_email_subject: "Join the Social Media Community" terms: "Government employees and contractors with an official .gov or .mil email are eligible to join." - members: 1,493 + members: 1,507 join_cop_button: "Social Media community members" kicker: "Join the Social Media Community" diff --git a/content/communities/user-experience.md b/content/communities/user-experience.md index 8976d4ff72..826396fad2 100644 --- a/content/communities/user-experience.md +++ b/content/communities/user-experience.md @@ -35,7 +35,7 @@ community_list: subscribe_email: "ux-cop-request@listserv.gsa.gov" subscribe_email_subject: "Join the User Experience Community" terms: "Government employees and contractors with an official .gov or .mil email are eligible to join." - members: 2,237 + members: 2,277 join_cop_button: "User Experience community members" # see all authors at https://digital.gov/authors diff --git a/content/communities/web-analytics-and-optimization.md b/content/communities/web-analytics-and-optimization.md index ce82041f3f..af3e78e703 100644 --- a/content/communities/web-analytics-and-optimization.md +++ b/content/communities/web-analytics-and-optimization.md @@ -42,7 +42,7 @@ community_list: subscribe_email: "analyze-optimize-request@listserv.gsa.gov" subscribe_email_subject: "Join the Web Analytics Community" terms: "Government employees and contractors with an official .gov or .mil email are eligible to join." - members: 1,018 + members: 1,045 join_cop_button: "Web Analytic community members" kicker: "Join the Web Analytics Community" diff --git a/content/communities/web-managers-forum.md b/content/communities/web-managers-forum.md index 5bcac9e6dc..491997effe 100644 --- a/content/communities/web-managers-forum.md +++ b/content/communities/web-managers-forum.md @@ -66,7 +66,7 @@ community_list: subscribe_email: "content-managers-l-request@listserv.gsa.gov" subscribe_email_subject: "Join the Web Managers Community" terms: "Government employees and contractors with an official .gov or .mil email are eligible to join." - members: 2,130 + members: 2,160 join_cop_button: "Web Manager community members" primary_image: "white-on-gsa-blue-digital-gov-card-community" diff --git a/content/events/2019/04/2019-04-25-indexing-with-searchgov.md b/content/events/2019/04/2019-04-25-indexing-with-searchgov.md index 47e412e07d..78e8933b0f 100644 --- a/content/events/2019/04/2019-04-25-indexing-with-searchgov.md +++ b/content/events/2019/04/2019-04-25-indexing-with-searchgov.md @@ -50,7 +50,7 @@ This talk is hosted by Search.gov and Digital.gov. [**Search.gov**](https://search.gov/) is a plug-and-play search solution that federal agencies can use to provide a high-quality search experience to their site’s users. Search.gov is a service of the General Services Administration, supporting the search boxes on over 2,000 websites, across 30% of federal domains. -For more information about Search.gov, email [search@support.digitalgov.gov](mailto:search@support.digitalgov.gov), or call 202-969-7426. +For more information about Search.gov, email [search@gsa.gov](mailto:search@gsa.gov), or call 202-969-7426. Questions about this event or future events? Send them to [digitalgov@gsa.gov](mailto:digitalgov@gsa.gov). diff --git a/content/events/2019/06/2019-06-12-introduction-searchgov.md b/content/events/2019/06/2019-06-12-introduction-searchgov.md index a2269a3695..0c870364e5 100644 --- a/content/events/2019/06/2019-06-12-introduction-searchgov.md +++ b/content/events/2019/06/2019-06-12-introduction-searchgov.md @@ -43,6 +43,6 @@ In this online event, we’ll provide a basic overview of Search.gov’s essenti [**Search.gov**](https://search.gov/) is a service of the General Services Administration, supporting the search boxes on over 2,000 websites, across 30% of federal domains. -For more information about Search.gov, email [search@support.digitalgov.gov](mailto:search@support.digitalgov.gov), or call 202-969-7426. +For more information about Search.gov, email [search@gsa.gov](mailto:search@gsa.gov), or call 202-969-7426. _Questions about this event or future events? Send them to [digitalgov@gsa.gov](mailto:digitalgov@gsa.gov)._ diff --git a/content/events/2024/06/2024-06-20-uswds-monthly-call-june-2024.md b/content/events/2024/06/2024-06-20-uswds-monthly-call-june-2024.md index 39eee61f6f..e379b180ae 100644 --- a/content/events/2024/06/2024-06-20-uswds-monthly-call-june-2024.md +++ b/content/events/2024/06/2024-06-20-uswds-monthly-call-june-2024.md @@ -19,11 +19,287 @@ slug: uswds-monthly-call-june-2024 event_platform: zoom primary_image: 2024-uswds-monthly-call-june-title-card +youtube_id: yBCLyWO0yCo --- {{< asset-static file="uswds-monthly-call-june-2024.pptx" label="View the slides (Powerpoint presentation, 3 MB, 33 slides)">}} +{{< accordion kicker="Slide by Slide" title="USWDS Monthly Call - Presentation Script for May 2024" icon="content_copy" >}}**Slide 1:** Thanks Jeannie, and welcome, everyone, to the U.S. Web Design System monthly call for June 2024 — this month we're celebrating Juneteenth (just yesterday!) with the USWDS logo in red, white, and blue. We're also celebrating Pride month in June, with the USWDS logo showing a selection of Pride flags, including the classic rainbow pride flag; the Philadelphia City Hall pride flag, with black and brown stripes in addition to the rainbow; transgender pride in blue, pink, and white; nonbinary pride in yellow, white, purple, and black; and the intersex pride flag with a purple circle on yellow. Today is also the summer solstice here in the northern hemisphere — the longest day of the year — with the USWDS logo in bright yellow like the sun. + +**Slide 2:** My name is Dan Williams, he/him, and I'm the USWDS project lead — and here on-screen is my avatar: dark hair, blue sweater, collared shirt. And I've still not updated this avatar to feature the glasses I now wear in real life, in addition to a blue collared shirt, and gold socks. + +As Jeannie mentioned, we are recording this call, and I'm happy to say we've started to be able to share the recordings of these monthly calls publicly. You can find pretty much everything from the last year's worth of monthly calls — back to January 2023 — on our website, at [designsystem.digital.gov/about/monthly-calls](https://designsystem.digital.gov/about/monthly-calls/). We typically post videos shortly after the monthly call, and we also link out to the slides and the script, hosted at digital.gov. We've also started collecting each month's Q&A as a GitHub discussion linked from the monthly calls page. We've posted a link to our monthly calls page in the chat. + +We'll be posting other links and references into the chat as we go along, and I encourage you to ask questions in the chat at any time. If any member of our team can answer your question in the chat, we'll do so. Otherwise, there'll be some time for questions and answers at the end of the hour. Also, be sure to introduce yourself in the chat as well — it's nice to know who's here. It's good to have you here today. + +For those of you who find the chat distracting, you’re welcome to close or hide the chat window during the main presentation. You can reopen it later during the Q&A session at the end of this call. So thanks! And, with that, let's get started! + +**Slide 3:** So what's our agenda for today? We’ve got a couple of new sites to feature and a few product updates. Then, we're going to give a first look at a couple of USWDS web components to begin to give a sense of how this new technology could work in practice. Then, we will hopefully leave plenty of time for Q&A. + +**Slide 4:** So let's get into it with featured sites. + +**Slide 5:** First, we've got a new site from the Department of Treasury's Export Compliance Assistance Program (or ECAP): [nnsa.nsis.anl.gov/ecap](https://nnsa.nsis.anl.gov/ecap/). The ECAP provides export compliance training, assistance, and tools to raise awareness and promote export compliance across the Department of Energy and National Nuclear Security Administration complex. The ECAP homepage has a familiar USWDS look and feel, and its most prominent aspect is an alert, informing its audience of severe restrictions for all interactions with Russia and Belarus. + +**Slide 6:** And this month we're also highlighting [acc.gov](https://acc.gov): a site for the American Climate Corps, an organization inspired by the Civilian Conservation Corps of the 1930s, that seeks to empower a new, diverse generation to tackle environmental injustice and climate change. The ACC home is an interesting example of customizing USWDS to achieve a distinctive look and feel tailored to a specific audience. The ACC homepage features a large mint-toned hero section with an illustration of wind turbines and the words "Your climate career starts here." + +**Slide 7:** Congratulations and great work! Be sure to let our team know when a new site launches, either with an email or a note, on the USWDS public Slack channel! + +**Slide 8:** Now for a few product updates… + +**Slide 9:** First, out now: [USWDS 3.8.1](https://github.com/uswds/uswds/releases/tag/v3.8.1), a small but mighty new release that addresses a couple important bugs. + +**Slide 10:** Here are a couple of the key improvements in USWDS 3.8.1. The biggest is a couple of accessibility bugs that we've addressed with the tooltip. Now, the tooltip stays open when users are hovering over it — even if they're not hovering over the tooltip trigger. And additionally, users can now always dismiss a tooltip with the **escape** key. + +We also fixed an input mask initialization bug that caused it to fail if it wasn't a direct child of a form. + +We also improved the display of nested button groups. Now, nested button groups should match the height of their parents. + +And finally, we fixed an issue in our documentation where a data error introduced a typo in the Spanish translation of the Identifier in USWDS 3.7.0. + +And those are the big updates in USWDS 3.8.1, which is out now! + +**Slide 11:** There's also always something going on in our public discussions on GitHub. Today, I'd like to call out four discussions of note. + +There's a new [discussion open now for a Chat component](https://github.com/uswds/uswds/discussions/5938). So if you've worked on a chat UI, or have some interest in making the case for chat-related components in USWDS, contribute to building out the proposal for Chat in its discussion. + +We've also had some new activity in the [Glossary component chat](https://github.com/uswds/uswds/discussions/5771) as well, with folks providing some good examples of implementing Glossary functionality, along with some discussion about accessibility considerations of different approaches. So if you have experience or thoughts working on a Glossary solution, check out this discussion. + +We also have a new accessibility discussion for June. [We are digging into how folks are using the Tag component](https://github.com/uswds/uswds/discussions/5956). We're interested in knowing the different types of uses for _interactive_ tags: that is, tags that are not only used to call out a category or special context for content, but those that have some kind of interactive element (whether it's linking off to similarly tagged content, filtering something like search results, or something else). If you use tags, let us know! + +And finally, as I alluded to earlier, we have [the Q&A from our May monthly call](https://github.com/uswds/uswds/discussions/5952) posted in GitHub discussions — so if you couldn't make the last call, you can catch up on some of the items that came up as questions from the audience. + +**Slide 12:** And now an update on some of our ongoing usability research. + +Some of you may remember the research project we undertook last year focussed on the most important things people need to be able to do on our website: what we call our users’ **Top Tasks**. (If you're interested, we talked about this in our [April 2023 call](https://designsystem.digital.gov/about/monthly-calls/#april-2023-top-tasks), linked in the chat.) Well, once we identified those top tasks, we wanted to find out how easy or difficult it is for folks to actually do those things on our website. That’s what we just wrapped up: testing the performance of those Top Tasks. + +**Slide 13:** We looked at 5 Top Tasks in this study: + +1. Browsing components +2. Finding specific component guidance +3. Finding specific pattern guidance +4. Finding examples of sites that use USWDS… and +5. Getting design kits. + +**Slide 14:** We did 45-minute moderated usability testing sessions with 7 people who use the design system. Most of them described themselves as designers and researchers, with a couple of folks with a developer background, and 1 person who was a product manager. We expect to run another round of usability testing focusing on developers, but for this round, we focused on designers. + +All participants described their proficiency level with USWDS between novice (with very little experience using it) to intermediate, which was great because we were trying to get the perspectives of folks who were newer to the design system — coming with fresh eyes. Participants came from 7 different agencies, and it was great to get such a variety represented. + +Let’s talk about our high-level findings. + +**Slide 15:** We observed that the **most challenging tasks** were browsing components and finding pattern guidance. The reasons people seemed to struggle with these tasks had a lot to do with confusion about the words we use to describe things. + +For instance, we don't always share a common understanding of what a "pattern" is. Is it a repeating, decorative motif? Is it a functional unit like a component? Is it an interaction? + +Additionally, we don't always have common words for specific components. In one of the task scenarios we wanted folks to find the Step Indicator component, but none of the participants used that term themselves. Instead, they were looking for “progress bar” or “progress indicator.” Or instead of “in-page navigation” they were looking for “anchor links” or “jump links.” + +There was some **mixed success** in finding _specific_ component guidance — usability guidance within a component's page — with one of the scenarios being easy for people to complete (finding accessibility guidance for the Button component) and the other being more difficult (finding accessibility guidance for Data Visualization). + +The **most successful tasks** were finding examples of other sites that use USWDS as well as finding the design kits. Still, we got some great insight into how to make those tasks even easier, since people still struggled with the design kit file feeling buried on its page. + +It’s also hard for people to find the full showcase list of sites that use USWDS and there’s room to improve how to present that list, as well. + +**Slide 16:** If we had to boil it down to one top takeaway from this study, it’s that **the way we name things doesn’t always match user expectations**. Participants often didn't intuitively understand the top-level navigation and weren’t sure what to expect in each category. + +**Slide 17:** So, we have some actionable insights and clear next steps to help make some of these Top Tasks easier for folks. We'll be getting new issues on our project board, and working on prioritizing some solutions. Some of these solutions are closer to low-hanging fruit, like increasing the prominence of design kit files, or adding better component synonyms on the component filter page. + +Some solutions will take more thought and research, like rethinking top-level organization and finding increasingly clearer ways to talk about patterns. We'll have these things in mind as we move toward a new major version of the design system next year. + +**Slide 18:** And finally, this month we've published five more accessibility test pages: [Banner](https://designsystem.digital.gov/components/banner/accessibility-tests/), [Search](https://designsystem.digital.gov/components/search/accessibility-tests/), [Select](https://designsystem.digital.gov/components/select/accessibility-tests/), [Text Input](https://designsystem.digital.gov/components/text-input/accessibility-tests/), and [Tooltip](https://designsystem.digital.gov/components/tooltip/accessibility-tests/). We've posted links to these new pages in the chat, and we're working on three big component test pages for next month: Header, Footer, and Identifier. So stay tuned for those! + +**Slide 19:** Phew! We've had a lot going on over the last month, and we haven't even gotten to the main event: A first look at USWDS web components. + +**Slide 20:** As we're making our initial progress here, this work is very much in an alpha stage. What exactly is the alpha stage? Well, we're still trying to define exactly it for ourselves, but at its broadest, it's an _experimental_ stage — a stage where we explicitly value exploration over refinement. + +It's a place where things can and will change without warning, where we give ourselves the room to try a bunch of stuff out. Traditionally, it's an internal phase, but there's plenty of value in making this work visible. Just don't rely on it yet! + +**Slide 21:** From the start, however, there are a few general values we're trying to keep in mind as we work. The first is Simplicity: we're focused on delivering components that are as easy as possible to install and use in a project, components that don't require any initial compilation or knowledge of third-party technologies like Sass to get going. + +**Slide 22:** We also have encapsulation in mind. We want designers and developers to have reliable performance wherever they use a component. Encapsulation in Web Components means that everything _you need_ to use the component is built into the element: including not just the interactivity, but also the styles. Web Components don't require an external stylesheet for basic functionality. In the Web Components world, we see the primary use for CSS in the _setting_ of CSS variables or CSS _custom properties, not_ in any necessary or additional CSS _rules_. CSS still has an important role to play, but not for default display and functionality. + +And this also means that USWDS Web Components will be largely unaffected by any existing styles on your site — and that include styles associated with non–Web Component versions of USWDS. _Traditional_ USWDS and _Web Components_ USWDS will be able to exist together, in parallel, on a project. And we expect that that will happen frequently. + +**Slide 23:** Relatedly, we're thinking about our components through the lens of configuration — how to use component attributes and CSS variables to control theming instead of custom CSS rules. We see the future of USWDS theming as closer to a settings file than a stylesheet. + +**Slide 24:** And finally, we have a platform orientation. We're trying to deliver a design system that's built with native, web standards-based solutions that support the web as a platform. HTML, CSS, and JavaScript have changed since the early days of USWDS, and while we've not lost sight of those changes as USWDS has evolved over the years, this new version is our best opportunity to really embrace the way the web as a platform has also matured and evolved, and to position ourselves to _continue_ to evolve alongside it. + +**Slide 25:** So let's take a look at what we're building. And to do that, I'd like to introduce a couple of my colleagues. First James Mejia. James is an engineer and a contractor on the USWDS Core team. James, can you introduce yourself and give a brief self description for anyone audio-only? + +James: Yes, of course. Thanks, Dan. Hello, everyone. My name is James Mejia. I have short dark hair, brown skin, and a blue button-up. Happy to be here today. + +**Slide 26:** Dan: Thanks James! I'd also like to introduce Matt Henry. Matt's our new Engineering Lead, and we're very pleased to have him on the USWDS team. Matt, can you introduce yourself and give a brief self description for anyone audio-only? + +Matt: Hello! I'm Matt Henry, I use he/him pronouns and I'm an engineer at 18F currently acting as engineering lead for USWDS. For accessibility purposes, I'm a bald white man with glasses and a close-cropped white beard. I've been working with the Design System for a couple of months now to help bring the next version into being, and I'm super excited to show you some of what we've been working on. + +**Slide 27:** Dan: Thank you Matt! So let's see this is in action. Matt and James will take the demo from here, starting with Matt. Matt, why don't you share your screen and take it away! + +Matt: First, by way of disclaimer, I should say that — as Dan mentioned — everything you see here today is in either an alpha or pre-alpha state, so some things about how this is all going to work will change by the time we ship code that you'll want to include in your projects. + +This is going to be sort of a re-introduction to USWDS so let's go back to basics with the USWDS tutorial project that you might recognize from some of these monthly calls. I'm going to start with a clean install of the tutorial so you can see everything I'll need to do to get something working. You may have gone through this tutorial before, so it may be familiar. + +Now just to prove there's nothing up my sleeve, here's a clean git status on the main branch of the tutorial. We originally developed this tutorial to walk through installing the design system and a couple components on a site that doesn't use USWDS. So not only does this fresh install not have USWDS on it already, but it's a great way for us to show how we might install a Web Components–based version of USWDS. + +The first task the tutorial wants you to take on is adding the banner. This is the component you've probably worked with many many times that tells users that they're on an official government site, so it's a good place to start! Now let's get going and install the banner component. +`# in uswds-tutorial project npm link @uswds-next/usa-banner` + +What we just did is a little npm magic, and this is the step where, if the component was published on npm, which it will be eventually, you'd just `npm install`. So you can just imagine I did `npm install @uswds/usa-banner`, since that's what you'll be doing. + +The tutorial site is built with eleventy. You don't need to know anything about eleventy to follow along with what I'm doing, but I'm pointing it out because the next thing I'll do is just a tiny bit of eleventy configuration to make use of the component we just installed. + +`eleventyConfig.addPassthroughCopy({ + "node_modules/@uswds-next/usa-banner/dist/usa-banner.js": + "assets/usa-banner.js", + });` + +All that does is copy the component code we just added to the project out of the node_modules and into the assets folder in the built site. If you use eleventy you might do something similar. + +Now I’m going to go ahead and spin up the 11ty dev server. But now that we've done that setup, we're ready to add the banner to the site, so let's do it. + +` +` + +That's literally it. And the only reason we even needed to touch the eleventy configuration is because we installed a local copy of the component, so we needed to get that code out of the `node_modules` and into the website itself. + +If we were using a cloud-hosted version of the component, we wouldn't even need to do that, and all we would need to do is add the script tag and we could use the `` tag straight away. + +Now let's see what we get out of the box: + +* The banner toggle just works. +* CSS is included. +* All of the images are part of the build. + +Let's jump back over to the tutorial project to see what we _didn't_ have to do to get this working. We _didn't_: + +* Install `@uswds/compile` +* Configure gulp +* Mess with theme settings +* Touch any image paths + +This is all done at build time when we bundled the package. + +What are some other things we can do with this banner? How about if we wanted our banner to be in spanish? _We sure can!_ + +` +` + +_Simple!_ Now just to prove that this is just a plain-old HTML element, let's go do some JavaScript on it. + +`const banner = document.querySelector('usa-banner')` + +See how we can grab the banner element with the same DOM API methods you use to do anything else with HTML. `toggle` is just a method on the `UsaBanner` class, and it's part of the public API for that class, so you can call it yourself programmatically if you need to, like so: + +`banner.toggle()` + +Now that we’ve seen it in action, first, let's go over to the code for the web-component version of the Design System and look under the hood at the banner we’ve been working with. Just a few quick things I want to point out here: + +Right here at the top, we're importing a bunch of things from a package called **Lit**. Lit is a lightweight library that gives you some useful tools for creating and working with HTML custom elements (aka Web Components—I'll use custom elements and web components interchangeably). + +Right away, where we create this component, we're making a new class that extends from `LitElement`. If we were making a component without a library, we'd be extending `HtmlElement` directly, but as I said, Lit gives us some superpowers to make all of this easier and less verbose. If you're curious about Lit and want to learn more about why we're using it, there's some documentation about it in the USWDS Proposals Repo. + +Scrolling quickly through here, you can see a bunch of JS, CSS, and HTML. A whole bunch of this is just taken directly from the existing banner component. And here in the class body, you can see that `toggle` method we used before. All it does is flip the boolean value of the `isOpen` prop, and then toggles the `hidden` attribute. + +Then, way at the bottom, there's this call to `customElements.define`. This is a web standard method for taking the class that we implemented above and telling the browser what the HTML tagname we're going to use to actually bring all of this functionality into our web page. We're talking about the banner here, so we're stuffing all of this code into a tag called `usa-banner`. You can tell it's a web component because of the dash. Part of the standard is a requirement that all custom element tag names include a dash to set them apart from the ones that ship in the browser. + +So that concludes our whirlwind tour of the banner code itself. + +While we're here in the uswds-next repository, I can show you how I did the production build of our component we’ve been using. I should be clear up front here that this build and link stuff I'm doing is just for demonstration purposes and it's not anything that you'll need to know. + +`# in uswds-next repo root +npm run build --workspace=@uswds-next/usa-banner +npm link --workspace=@uswds-next/usa-banner` + +You'll notice that the component we're building here is just the banner by itself. We're planning to ship each component as its own package, so you can use them _a la carte_ as it were and only bring in the components you need for your project. It'll also let you add new versions of the components as we ship them, so you don't need to wait until we have the whole design system rebuilt in web components before you start working with them. + +Ok, let’s get back to demoing. We saw before with the language in the banner how we can customize components using attributes. Those attributes are basically part of the API that you'll use to interact with and customize components. What about theming and visual style? + +To answer that, I'm going to use another component: the humble but mighty `usa-link`. I've already built and linked that, so we can go ahead and add it to the project the same way we did with the banner: + +`eleventyConfig.addPassthroughCopy({ + "node_modules/@uswds-next/usa-link/dist/usa-link.js": "assets/usa-link.js", + });` + +and then add it to the page: + +`` + +`It's dangerous to go alone. Take this!` + +We've got a link! Because the link component has USWDS styles baked into it, we can see that when the link is active, it has the big blue focus outline you'd expect to see. But what if we wanted to change that? Let's crack the component open and see what's inside there that we might want to style. Here's our link component. If we expand it, we can see there's a plain old link inside with the `.usa-link` class, and we know how to style things that have classes… don't we? + +So you might think you'd be able to just write some CSS like this: + +` ` + +Alas, this doesn't work. And the reason it doesn't work is this `#shadow-root` thing. That link in there is part of what's called the Shadow DOM. + +In a nutshell, Shadow DOM is just markup that the component writes. You can contrast shadow DOM with Light DOM, which is the markup you write. But what's important here is that styles from outside of the shadow DOM don't apply inside of it. A lot of the time, this is what we want! This provides a layer of encapsulation that protects your component from being styled by whatever styles happen to match it on whatever page your component ends up on. + +But it also means that styling components is just a little bit trickier than you're used to. Fortunately, there are a couple of ways for styles to make their way into the shadow DOM. One of those is with CSS custom properties, or CSS variables. Unlike most CSS properties, the values of CSS variables do cascade into the Shadow DOM. So anything we set in a CSS variable will be available for our component to use. + +So if we go over to the code for the link component, we can see the CSS it uses here. You'll notice that most of the styles use CSS variables. Those variables are part of the API surface for customizing components. And if we look at the focus styles, we can see a variable that looks a lot like the thing we want to change, so let's go change it and see what happens… + +`` + +It worked! So what we've seen here is that there are a couple of ways to customize components. First there are attributes, which work similarly to the props you might be used to if you're coming from something like React or Vue, and there are CSS variables that let you apply themes or do other kinds of look & feel customization. You can think of attributes and CSS variables as being part of the public API for web components. + +They're basically the levers and switches that component authors build into their components to let you customize them in predefined ways. There are other ways to extend and customize components, but we'll dig into those in a future Monthly Call. + +For now, I want to hand demo responsibilities over to James Mejia who will show off our new Storybook setup, and some of the variants of the handful of components we're currently working on. + +James: Thanks Matt. We started using Storybook in USWDS 3 for internal development. The work we do in the USWDS repo eventually appears on the website, which has the full guidance. This isn’t a bad process, but it separates the user facing documentation from the code. + +As we move forward with USWDS 4 we want to explore ways to add more relevant documentation in Storybook. For a good part of our web component development this is where our documentation will live. + +As you can see with USA Banner we’re taking advantage of the **autodocs** plugin to create a single page with the component settings. As we scroll down we can see a preview of all of the available variants. + +We want every component to have controls so that users can test these components before using them. You can see the default Banner includes `label, tld` (Top Level Domain), and `lang` settings. We can modify these settings to update the component. + +Let’s modify the lang field to `es` and watch the component change to the Spanish variant. + +Next, let’s checkout the Custom Aria label story. + +It’s hard to see here, but the `label` setting allows us to update the Banner’s aria label. This is useful when we’re using the custom content variant, which uses slots. If we were using a different language outside of English or Spanish, we can update the aria label to match the new language. + +If we click on the Custom Content Banner variant we’ll see even more controls. + +Six new controls appear that allow us to modify content in real time. For example, `bannerText`. + +We have more work to do, but our goal will be to have more relevant documentation and controls in our component library. That way you have everything you need in one place. + +The other components follow a similar pattern. For example, USA Identifier on the screen. We’re trying to document all of the ways you can use it. + +As I mentioned earlier, we want to improve documentation and show developers what components offer. We also want to document how we develop components for ourselves and contributors. + +We hope that our documentation work in Storybook will be more relevant and publicly available in the future. Thanks everyone and now I’ll pass it back to Dan. + +**Slide 28:** Dan: This is Dan. Thanks, Matt, and thanks, James. There's still significant work ahead of us, but I think even our first steps are pointing in a clear and exciting direction. If you're interested in this work and want to follow along with our progress, we have a new and very alpha GitHub repo where this new work lives. You can find it at [github.com/uswds/uswds-next](http://github.com/uswds/uswds-next). I expect it will see some big changes over the next few months — it's a bit like a new USWDS young adult is moving out of its parent house and into its first new place. We hope to grow up pretty quickly. + +**Slide 29:** So what's next with this project? In the next couple of months, we're going to work to get these alpha components into the Beta phase and publish them on npm. We're also going to begin work on a handful of other components. I currently expect these to be Button, Accordion, Card, Text Input, and Modal. This next handful of components should be in a Beta phase by September. + +**Slide 30:** And then, by the end of the fall, we should have at least a dozen components in stable Beta. We're going to be adding engineers to the team as well, so our progress should start to speed up as we develop our Web Components conventions and tooling. This initiative is what it's all about for us for the foreseeable future, so expect plenty of updates in these monthly calls, as well as in our newsletter, and on GitHub. + +**Slide 31:** So onward. This is the first step of many. Here we go. + +**Slide 32:** And it looks like we have some time for Q&A, so let's get to it. + +**Slide 33:** Thanks for joining today’s USWDS monthly call. Next month is our summer break from monthly calls, so we won't meet again until August, when we're expecting a community choice monthly call. Let us know what you'd like to hear about on the [community choice monthly call discussion thread](https://github.com/uswds/uswds/discussions/5923). We're posting the link here in the chat! + +Please look out for an event feedback survey from Digital.gov. You'll get this in your email, and there's also a link in the chat. Your feedback makes a difference to us, so we'd appreciate the extra time it takes you to provide it. + +And if you have a question we weren't able to answer in the call, or thought of later, please head into our public Slack and ask it there. We'll be around after the call to answer questions. + +Have a great day and a great summer. Make sure you check out our August Community Choice discussion, so we get a sense of what you'd like us to prepare, and we'll see you in August! + +{{< /accordion >}} + Join the U.S. Web Design System (USWDS) team to see their progress toward developing web components for the design system. They’ll demo early working prototypes of a few components, and talk about some of the design questions they’re trying to answer through the prototyping process. In this online session with the USWDS team, you will: diff --git a/content/guides/dap/_index.md b/content/guides/dap/_index.md index c540c96573..94e7638d7f 100755 --- a/content/guides/dap/_index.md +++ b/content/guides/dap/_index.md @@ -5,10 +5,13 @@ deck: "A free analytics tool for measuring digital services in the federal gover summary: "The Digital Analytics Program (DAP) offers advanced, easy web analytics for federal agencies." guide: dap +# See all topics at https://digital.gov/topics topics: - analytics - digital-service-delivery - user-experience + +# Redirects: Enter the paths of the old URLs that you want redirected to this page. aliases: - /guide/dap/ - /guides/dap/become-a-dap-certified-analyst/ @@ -22,14 +25,20 @@ aliases: - /guides/dap/gaining-access-to-dap-data/ - /guides/dap/guidelines-for-sharing-dap-data/ -image: guide-dap - +# For social media shares; if not provided, the default Digital.gov logo card appears. primary_image: guide-dap -weight: 1 guide_weight: 5 +image: guide-dap layout: single +# Controls how this page appears across the site +# 0 -- hidden +# 1 -- visible +# 2 -- highlighted +# 10, 9, 8, 7, or 6 -- for the five items that appear in the white box on homepage; 10 is first or at the top, and 6 is fifth or last. +weight: 9 + --- ## What is DAP? diff --git a/content/guides/hcd/discovery-operations/research-checklist.md b/content/guides/hcd/discovery-operations/research-checklist.md index ea2b3195a4..67af5e21c1 100644 --- a/content/guides/hcd/discovery-operations/research-checklist.md +++ b/content/guides/hcd/discovery-operations/research-checklist.md @@ -7,7 +7,7 @@ primary_image: hcd-discovery-operations ---
-Review the }}">research section of the HCD Discovery Concepts Guide. +Review the [research section of the HCD Discovery Concepts Guide](https://digital.gov/guides/hcd/discovery-concepts/do-research/).
Check with the Logistics Coordinator for details on each interview site before going into the field. diff --git a/content/guides/public-policy/_index.md b/content/guides/public-policy/_index.md index f1539c543d..04e6adda9b 100755 --- a/content/guides/public-policy/_index.md +++ b/content/guides/public-policy/_index.md @@ -75,9 +75,9 @@ Keep reading to learn more about the different [types of policies](https://digit --- -
-

Footnotes

-
    -
  1. University of Cambridge. 2016. “Force Field Analysis.” Cam.ac.uk. University of Cambridge. 2016. https://www.ifm.eng.cam.ac.uk/research/dstools/force-field-analysis/.
  2. +
    +

    Footnotes

    +
      +
    1. University of Cambridge. 2016. “Force Field Analysis.” Cam.ac.uk. University of Cambridge. 2016. https://www.ifm.eng.cam.ac.uk/research/dstools/force-field-analysis/.
    -
    +
    diff --git a/content/guides/public-policy/accessibility.md b/content/guides/public-policy/accessibility.md index 661a677c42..12000cf8cd 100644 --- a/content/guides/public-policy/accessibility.md +++ b/content/guides/public-policy/accessibility.md @@ -160,9 +160,9 @@ Focusing on our product roadmap, the Substance Abuse and Mental Health Administr --- -
    -

    Footnotes

    -
      -
    1. “World Wide Web Consortium Launches International Program Office for Web Accessibility Initiative.” 1997. W3C. October 22, 1997. https://www.w3.org/press-releases/1997/ipo-announce/.
    2. +
      +

      Footnotes

      +
        +
      1. “World Wide Web Consortium Launches International Program Office for Web Accessibility Initiative.” 1997. W3C. October 22, 1997. https://www.w3.org/press-releases/1997/ipo-announce/.
      -
      +
      diff --git a/content/guides/site-scanning/_index.md b/content/guides/site-scanning/_index.md index ff7b45f2d5..8d10ef570a 100644 --- a/content/guides/site-scanning/_index.md +++ b/content/guides/site-scanning/_index.md @@ -1,9 +1,15 @@ --- date: 2020-06-25 09:00:00 -0500 -title: "Handbook for the Site Scanning program" +title: "Understanding the Site Scanning program" deck: "A set of daily scans of the federal web presence." summary: "This program is available to automatically generate data about the health and best practices of federal websites." + guide: site-scanning +image: guide-site-scanning +layout: single +guide_weight: 3 + +# Redirects: Enter the paths of the old URLs that you want redirected to this page. aliases: - /guide/site-scanning/ - /site-scanning/ @@ -12,11 +18,22 @@ aliases: - /sitescan/ - /site-scans/ - /sitescans/ -image: guide-site-scanning + +# See all topics at https://digital.gov/topics +topics: + - analytics + - budgeting-and-performance + +# For social media shares; if not provided, the default Digital.gov logo card appears. primary_image: guide-site-scanning -layout: single -weight: 8 -guide_weight: 3 + +# Controls how this page appears across the site +# 0 -- hidden +# 1 -- visible +# 2 -- highlighted +# 10, 9, 8, 7, or 6 -- for the five items that appear in the white box on homepage; 10 is first or at the top, and 6 is fifth or last. +weight: 2 + --- **The Site Scanning program** automates a wide range of scans of public federal websites and generates data about website health, policy compliance, and best practices. diff --git a/content/news/2015/11/2015-11-20-ux-vs-cx-whats-the-dif-part-2.md b/content/news/2015/11/2015-11-20-ux-vs-cx-whats-the-dif-part-2.md index 065ab01384..09e0fc524f 100644 --- a/content/news/2015/11/2015-11-20-ux-vs-cx-whats-the-dif-part-2.md +++ b/content/news/2015/11/2015-11-20-ux-vs-cx-whats-the-dif-part-2.md @@ -62,9 +62,9 @@ How has your agency embraced CX and UX? Tell us in the comments. _Thanks to members of the [UX](https://digital.gov/communities/user-experience/) and the [CX](https://digital.gov/communities/customer-experience/) communities for their contributions to this article._ -
      -

      Footnotes

      -
        -
      1. CNN, By Doug Gross. n.d. “It’s Settled! Creator Tells Us How to Pronounce ‘GIF.’” CNN. https://www.cnn.com/2013/05/22/tech/web/pronounce-gif/
      2. +
        +

        Footnotes

        +
          +
        1. CNN, By Doug Gross. n.d. “It’s Settled! Creator Tells Us How to Pronounce ‘GIF.’” CNN. https://www.cnn.com/2013/05/22/tech/web/pronounce-gif/
        -
      +
diff --git a/content/news/2016/08/2016-08-18-the-right-tools-for-the-job-re-hosting-digitalgov-search-to-a-dynamic-infrastructure-environment.md b/content/news/2016/08/2016-08-18-the-right-tools-for-the-job-re-hosting-digitalgov-search-to-a-dynamic-infrastructure-environment.md index 697cd78825..dde8649099 100644 --- a/content/news/2016/08/2016-08-18-the-right-tools-for-the-job-re-hosting-digitalgov-search-to-a-dynamic-infrastructure-environment.md +++ b/content/news/2016/08/2016-08-18-the-right-tools-for-the-job-re-hosting-digitalgov-search-to-a-dynamic-infrastructure-environment.md @@ -35,7 +35,7 @@ While this series of posts will discuss the requirements above separately, the b 2. Build our servers and application within AWS 3. Cut over our read-only data center, allow dust to settle; then cut over our read-write data center, allow dust to settle; shut off old infrastructure -If you have questions or would like to discuss particulars, [we would love to hear from you](mailto:search@support.digitalgov.gov). +If you have questions or would like to discuss particulars, [we would love to hear from you](mailto:search@gsa.gov).

This is the first post in a five part series. Stay tuned for the following posts: diff --git a/content/news/2016/10/2016-10-21-dear-search-reading-between-the-lines-of-search-data.md b/content/news/2016/10/2016-10-21-dear-search-reading-between-the-lines-of-search-data.md index eccec5be81..fc3982fd55 100644 --- a/content/news/2016/10/2016-10-21-dear-search-reading-between-the-lines-of-search-data.md +++ b/content/news/2016/10/2016-10-21-dear-search-reading-between-the-lines-of-search-data.md @@ -27,7 +27,9 @@ And finally, do you have a formal method of evaluating the search experience tha Best, -UX Vexed {{< legacy-img src="2016/08/600-x-400-Search-bar-on-virtual-screen-Gajus-iStock-Thinkstock-178761722.jpg" alt="Search bar on virtual screen." caption="" >}} +UX Vexed + +{{< legacy-img src="2016/08/600-x-400-Search-bar-on-virtual-screen-Gajus-iStock-Thinkstock-178761722.jpg" alt="Search bar on virtual screen." caption="Gajus/iStock/Thinkstock" >}} Dear Vexed, @@ -45,7 +47,7 @@ There is a line of thinking that says if a site is designed optimally, then DigitalGov Search, like any search tool worth its salt, provides robust search analytics. We give views into to the top queries your site gets and the top pages clicked on out of your results pages. We also provide click-through rates for all queries: for all the times a query was run, how many clicks did that search results page get? You can see this for the top queries, or you can search for a particular term and see all the queries that contain that term. -{{< legacy-img src="2016/09/Queries\_-\_USA_gov-600w.png" alt="Screenshot of DigitalGov Search report on the top queries performed on USA.gov" >}} +{{< legacy-img src="2016/09/Queries_-_USA_gov-600w.png" alt="Screenshot of DigitalGov Search report on the top queries performed on USA.gov" >}} A really low click-through rate, or one well above 100%, is a sign that some sort of change would be helpful to searchers, but what that change is will depend on what they were looking for. @@ -53,16 +55,15 @@ Does a particular page need to be boosted in the search results, or do you need The next important question is: Where were the searchers when they ran a particular query? I’ve seen cases where a query was run directly from a page that, from the look of the url, should have contained the information the person was looking for. In our referrers report you can see the pages where the most queries were run on your site, and from there you can see all the queries that were run on any particular page. This is a gold mine for determining whether it’s your search configuration or your site content – or structure! – that needs tweaking. -{{< legacy-img src="2016/09/Referrers\_-\_USA_gov-600w.png" alt="The DigitalGov Search Top Referring URLs report, showing the pages on which people most frequently used the search box." >}} +{{< legacy-img src="2016/09/Referrers_-_USA_gov-600w.png" alt="The DigitalGov Search Top Referring URLs report, showing the pages on which people most frequently used the search box." >}} -Because the relationships between all these data points can be complex, we gave a webinar in December 2015 on analytics. Michelle Chronister, the former user experience team lead for USAgov, described how that team looks at search data for trends that can guide USAgov’s content strategy. They group query terms into topics, and analyze at the topic level. A }}" target="_blank">webinar recap is available that includes a video recording, slide deck, and data tracking/report template as well as an infographic of USAgov’s 2015 search traffic. +Because the relationships between all these data points can be complex, we gave a webinar in December 2015 on analytics. Michelle Chronister, the former user experience team lead for USAgov, described how that team looks at search data for trends that can guide USAgov’s content strategy. They group query terms into topics, and analyze at the topic level. A webinar recap is available that includes a video recording, slide deck, and data tracking/report template as well as an infographic of USAgov’s 2015 search traffic. -By looking at trends for individual queries, query topics, locations, and destinations, you’ve begun the journey towards using data to better inform your search configuration and content strategy. We also recommend looking at your website data from the }}" target="_blank">Digital Analytics Program or any other analytics software you have running on your site. +By looking at trends for individual queries, query topics, locations, and destinations, you’ve begun the journey towards using data to better inform your search configuration and content strategy. We also recommend looking at your website data from the Digital Analytics Program or any other analytics software you have running on your site. -If you have further questions, Vexed, you can always [email the DigitalGov Search team](mailto:search@support.digitalgov.gov). +If you have further questions, Vexed, you can always [email the DigitalGov Search team](mailto:search@gsa.gov). Happy searching, -Dawn - +Dawn
Program Manager, DigitalGov Search diff --git a/content/news/2017/02/2017-02-03-digitalgov-year-in-review-our-top-blogs-resources-and-writers-for-2016.md b/content/news/2017/02/2017-02-03-digitalgov-year-in-review-our-top-blogs-resources-and-writers-for-2016.md index 71161e2077..f52aac4e31 100644 --- a/content/news/2017/02/2017-02-03-digitalgov-year-in-review-our-top-blogs-resources-and-writers-for-2016.md +++ b/content/news/2017/02/2017-02-03-digitalgov-year-in-review-our-top-blogs-resources-and-writers-for-2016.md @@ -141,12 +141,12 @@ If you are a federal employee with a .gov or .mil email address interested in su - - - -
-

Footnotes

-
    -
  1. Genius artistry aside for just a femtosecond, both “Ziggy Stardust” and “The Kid” were Internet pioneers in the early and mid-1990s. They had the foresight to use the new medium to provide digital services and content to their customers; Prince in 1994, and David Bowie in 1998. Before their passing, each won web-related lifetime achievement awards that honor those who helped shape the Internet with their groundbreaking work, recognizing their influence and lasting impact on digital culture, communication, and technology.
  2. +
    +

    Footnotes

    +
      +
    1. Genius artistry aside for just a femtosecond, both “Ziggy Stardust” and “The Kid” were Internet pioneers in the early and mid-1990s. They had the foresight to use the new medium to provide digital services and content to their customers; Prince in 1994, and David Bowie in 1998. Before their passing, each won web-related lifetime achievement awards that honor those who helped shape the Internet with their groundbreaking work, recognizing their influence and lasting impact on digital culture, communication, and technology.
    -
+ - - - diff --git a/content/news/2020/08/2020-08-21-2-lessons-tech-leaders-can-learn.md b/content/news/2020/08/2020-08-21-2-lessons-tech-leaders-can-learn.md index 4a4f4e8a00..72d4abf75a 100644 --- a/content/news/2020/08/2020-08-21-2-lessons-tech-leaders-can-learn.md +++ b/content/news/2020/08/2020-08-21-2-lessons-tech-leaders-can-learn.md @@ -74,10 +74,10 @@ Are you passionate about the civic impact of communications and data? Interested _Adele Luta and Georgeta Dragoiu are Presidential Innovation Fellows detailed to the National Institutes of Health. Both joined PIF as private-sector industry leaders in their respective fields across STEM and communications._ --- -